{
  "openapi": "3.0.0",
  "paths": {
    "/api/v1/federation/group-mappings/sync": {
      "post": {
        "description": "Runs a full reconciliation of federated memberships against the last-known Entra attributes stored in Keycloak, applying the currently-configured group mappings. Does NOT poll Entra (login remains authoritative for the cloud OIDC path). Returns aggregated counts. Requires Super Admin privileges.",
        "operationId": "triggerFederationSync",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciliation completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FederationSyncResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Trigger federation reconciliation",
        "tags": [
          "Federation Group Mappings"
        ]
      }
    },
    "/api/v1/federation/group-mappings": {
      "get": {
        "description": "Lists federated group mappings across all organizations, optionally filtered by organization, provider, or enabled state. Requires Super Admin privileges.",
        "operationId": "listFederatedGroupMappings",
        "parameters": [
          {
            "name": "organizationId",
            "required": false,
            "in": "query",
            "description": "Filter by organization",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "provider",
            "required": false,
            "in": "query",
            "description": "Filter by identity provider",
            "schema": {
              "example": "ENTRA",
              "type": "string",
              "enum": [
                "ENTRA",
                "LDAP"
              ]
            }
          },
          {
            "name": "enabled",
            "required": false,
            "in": "query",
            "description": "Filter by enabled state",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mappings retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FederatedGroupMappingListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List federated group mappings",
        "tags": [
          "Federation Group Mappings"
        ]
      },
      "post": {
        "description": "Creates a federated group mapping tying a provider app-role / group to a WFM role within an organization. Requires Super Admin privileges.",
        "operationId": "createFederatedGroupMapping",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFederatedGroupMappingDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Mapping created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FederatedGroupMappingResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data or role does not exist"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          },
          "409": {
            "description": "Mapping already exists for this provider/externalId"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create federated group mapping",
        "tags": [
          "Federation Group Mappings"
        ]
      }
    },
    "/api/v1/federation/group-mappings/{id}": {
      "get": {
        "description": "Retrieves a single federated group mapping. Requires Super Admin privileges.",
        "operationId": "getFederatedGroupMapping",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Mapping UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mapping retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FederatedGroupMappingResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Mapping not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get federated group mapping",
        "tags": [
          "Federation Group Mappings"
        ]
      },
      "patch": {
        "description": "Updates a federated group mapping. Requires Super Admin privileges.",
        "operationId": "updateFederatedGroupMapping",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Mapping UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateFederatedGroupMappingDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mapping updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FederatedGroupMappingResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data or role does not exist"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Mapping not found"
          },
          "409": {
            "description": "Mapping already exists for this provider/externalId"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update federated group mapping",
        "tags": [
          "Federation Group Mappings"
        ]
      },
      "delete": {
        "description": "Disables (soft-removes) a federated group mapping so it is no longer resolved at login time, while preserving provenance for existing federated memberships. Requires Super Admin privileges.",
        "operationId": "deleteFederatedGroupMapping",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Mapping UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Mapping disabled successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Mapping not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Disable federated group mapping",
        "tags": [
          "Federation Group Mappings"
        ]
      }
    },
    "/api/v1/devices": {
      "get": {
        "description": "Retrieves a paginated list of devices in the caller’s organization. All filters are evaluated server-side and combined with AND: status, labels, labelKey, unlabeled, free-text search (name, clientId, reported device ID, serial number), CPU architecture (Margo rc2 enum) and deployed application version.",
        "operationId": "listDevices",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of devices to return",
            "schema": {
              "minimum": 1,
              "maximum": 1000,
              "default": 20,
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of devices to skip for pagination",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for cursor-based pagination (alternative to offset): the `nextCursor` device ID returned by the previous page",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by device status",
            "schema": {
              "example": "ONLINE",
              "type": "string",
              "enum": [
                "ONLINE",
                "OFFLINE",
                "PENDING",
                "ERROR"
              ]
            }
          },
          {
            "name": "labels",
            "required": false,
            "in": "query",
            "description": "Filter by device labels: a JSON-encoded object of exact key/value pairs, all of which must match (AND). Example: {\"environment\":\"production\",\"region\":\"us-west\"}. 1-20 entries, string values only. Malformed JSON or non-string values are rejected with 400.",
            "schema": {
              "example": "{\"environment\":\"production\",\"region\":\"us-west\"}",
              "type": "string"
            }
          },
          {
            "name": "labelKey",
            "required": false,
            "in": "query",
            "description": "Filter devices that carry this label key, with any value. Combines with `labels` (AND).",
            "schema": {
              "minLength": 1,
              "maxLength": 253,
              "example": "environment",
              "type": "string"
            }
          },
          {
            "name": "unlabeled",
            "required": false,
            "in": "query",
            "description": "When true, return only devices without any labels. Cannot be combined with `labels` or `labelKey`.",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Free-text search: case-insensitive substring match on device name, clientId, the Margo self-reported device ID and the Margo-reported serial number. The characters %, _ and backslash match literally.",
            "schema": {
              "minLength": 1,
              "maxLength": 100,
              "example": "edge-01",
              "type": "string"
            }
          },
          {
            "name": "architecture",
            "required": false,
            "in": "query",
            "description": "Filter by CPU architecture as reported by the device in its Margo capabilities (properties.resources.cpu.architecture). Margo rc2 enum.",
            "schema": {
              "example": "arm64",
              "type": "string",
              "enum": [
                "amd64",
                "arm64",
                "arm"
              ]
            }
          },
          {
            "name": "appVersion",
            "required": false,
            "in": "query",
            "description": "Filter devices that have at least one non-removed deployment of this exact application version",
            "schema": {
              "minLength": 1,
              "maxLength": 128,
              "example": "1.2.3",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of devices retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceListResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Malformed or contradictory query parameters (for example an architecture outside the Margo rc2 enum, invalid labels JSON, or unlabeled=true combined with labels)"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List devices",
        "tags": [
          "Devices"
        ]
      }
    },
    "/api/v1/devices/{id}": {
      "get": {
        "description": "Retrieves detailed information about a specific device.",
        "operationId": "getDevice",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Device retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get device by ID",
        "tags": [
          "Devices"
        ]
      },
      "patch": {
        "description": "Updates device properties such as name and labels.",
        "operationId": "updateDevice",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDeviceDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Device updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Invalid input data"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update device",
        "tags": [
          "Devices"
        ]
      },
      "delete": {
        "description": "Requests deletion of a device (FM-1359). Every deployment is first withdrawn from the device; the device stays listed with `deletionRequestedAt` set until it has confirmed the removals (or the removal timeout has passed), and is then soft-deleted. A device with nothing to withdraw is deleted at once.",
        "operationId": "deleteDevice",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Device deleted, or deletion requested while its deployments are withdrawn"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete device",
        "tags": [
          "Devices"
        ]
      }
    },
    "/api/v1/devices/{id}/activity": {
      "get": {
        "description": "Retrieves recent activity events for a specific device, including deployment status changes and certificate events.",
        "operationId": "getDeviceActivity",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of activity items to return",
            "schema": {
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Device activity retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecentActivityResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get device activity log",
        "tags": [
          "Devices"
        ]
      }
    },
    "/api/v1/devices/{id}/release-reported-device-id": {
      "post": {
        "description": "FM-1043: clears this device’s reportedDeviceId (the Margo self-reported DeviceId from its capabilities manifest) without soft-deleting the device. Recovery action for an ID-squatting collision — use when the rejection-audit trail shows this device is holding a DeviceId that belongs to another device, so the true owner’s next capabilities report can claim it.",
        "operationId": "releaseReportedDeviceId",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "reportedDeviceId claim released successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Release a reportedDeviceId claim",
        "tags": [
          "Devices"
        ]
      }
    },
    "/api/v1/devices/{deviceId}/certificates": {
      "get": {
        "description": "Retrieves all certificates for a specific device.",
        "operationId": "listDeviceCertificates",
        "parameters": [
          {
            "name": "deviceId",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of device certificates retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceCertificateListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List device certificates",
        "tags": [
          "Device Certificates"
        ]
      }
    },
    "/api/v1/devices/{deviceId}/certificates/{certificateId}": {
      "get": {
        "description": "Retrieves detailed information about a specific device certificate.",
        "operationId": "getDeviceCertificate",
        "parameters": [
          {
            "name": "deviceId",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "certificateId",
            "required": true,
            "in": "path",
            "description": "Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440001",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Device certificate retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceCertificateResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Device or certificate not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get device certificate",
        "tags": [
          "Device Certificates"
        ]
      }
    },
    "/api/v1/devices/{deviceId}/certificates/{certificateId}/revoke": {
      "post": {
        "description": "Revokes a device certificate. This action cannot be undone.",
        "operationId": "revokeDeviceCertificate",
        "parameters": [
          {
            "name": "x-user-id",
            "required": true,
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "deviceId",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "certificateId",
            "required": true,
            "in": "path",
            "description": "Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440001",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RevokeCertificateDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Certificate revoked successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceCertificateResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Invalid revocation reason"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions (operator or admin)"
          },
          "404": {
            "description": "Device or certificate not found, or certificate already revoked"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Revoke device certificate",
        "tags": [
          "Device Certificates"
        ]
      }
    },
    "/api/v1/devices/{deviceId}/certificates/{certificateId}/renew": {
      "post": {
        "description": "Device certificate renewal is currently disabled and always fails with 409 Conflict. A renewed certificate has to reach the device, and Margo v1.0.0-rc2 defines no mechanism to deliver one (tracked as FM-1318). The device keeps presenting the certificate it onboarded with; the stored validity always reflects that certificate.",
        "operationId": "renewDeviceCertificate",
        "parameters": [
          {
            "name": "deviceId",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "certificateId",
            "required": true,
            "in": "path",
            "description": "Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440001",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenewCertificateDto"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Bad Request - Invalid path parameter or request body"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions (operator or admin)"
          },
          "409": {
            "description": "Conflict - Device certificate renewal is disabled: Margo v1.0.0-rc2 defines no mechanism to deliver a renewed certificate to the device (FM-1318)."
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Renew device certificate (disabled)",
        "tags": [
          "Device Certificates"
        ]
      }
    },
    "/api/v1/deployments": {
      "get": {
        "description": "Retrieves a paginated list of deployments with optional filtering by status, name, search, device, application version, application, rollout strategy and creation date range.",
        "operationId": "listDeployments",
        "parameters": [
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by deployment status",
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "INSTALLING",
                "INSTALLED",
                "FAILED",
                "REMOVING",
                "REMOVED"
              ]
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search in deployment name and status message",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "required": false,
            "in": "query",
            "description": "Filter by deployment name (contains, case-insensitive)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "deviceId",
            "required": false,
            "in": "query",
            "description": "Only deployments that have an instance targeting this device",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "applicationDescriptionId",
            "required": false,
            "in": "query",
            "description": "Only deployments of this application description (version)",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "applicationPackageId",
            "required": false,
            "in": "query",
            "description": "Only deployments whose application description belongs to this application",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "strategy",
            "required": false,
            "in": "query",
            "description": "Filter by rollout strategy",
            "schema": {
              "type": "string",
              "enum": [
                "IMMEDIATE",
                "CANARY",
                "ROLLING"
              ]
            }
          },
          {
            "name": "createdFrom",
            "required": false,
            "in": "query",
            "description": "Inclusive lower bound on createdAt. ISO 8601 `YYYY-MM-DD` (00:00 UTC) or a date-time with a zone.",
            "schema": {
              "example": "2026-01-01",
              "type": "string"
            }
          },
          {
            "name": "createdTo",
            "required": false,
            "in": "query",
            "description": "Inclusive upper bound on createdAt. A date-only value (`YYYY-MM-DD`) includes the whole UTC day. createdFrom later than createdTo is rejected with 400.",
            "schema": {
              "example": "2026-01-31",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Page size",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of results to skip",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for pagination",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of deployments retrieved successfully"
          },
          "400": {
            "description": "Bad request - a query parameter is malformed, or createdFrom is after createdTo"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "503": {
            "description": "Service unavailable - Deployment service is not reachable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List deployments",
        "tags": [
          "Deployments"
        ]
      },
      "post": {
        "description": "Creates a new deployment.",
        "operationId": "createDeployment",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Deployment created successfully"
          },
          "400": {
            "description": "Bad request - Invalid deployment data"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create a deployment",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/stats": {
      "get": {
        "description": "Retrieves deployment statistics for the organization.",
        "operationId": "getDeploymentStats",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployment statistics retrieved successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get deployment statistics",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/awaiting-removal": {
      "get": {
        "description": "Lists deployments that were deleted but that at least one device has not yet confirmed removing. A deployment leaves this list only when every device has reported `removed`; a device that reported `failed` keeps it here, because the workload may still be running.",
        "operationId": "listDeploymentsAwaitingRemoval",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployments awaiting removal retrieved successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List deployments awaiting removal confirmation",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/preview-targets": {
      "post": {
        "description": "Returns the unique device set, with provenance, that a create with the same target inputs would materialize. Reads live group membership.",
        "operationId": "previewDeploymentTargets",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PreviewDeploymentTargetsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resolved targets"
          },
          "400": {
            "description": "Invalid body or unknown device group"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Preview deployment targets",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}": {
      "get": {
        "description": "Retrieves detailed information about a specific deployment.",
        "operationId": "getDeployment",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployment retrieved successfully"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get deployment by ID",
        "tags": [
          "Deployments"
        ]
      },
      "put": {
        "description": "Updates an existing deployment.",
        "operationId": "updateDeployment",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployment updated successfully"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          },
          "409": {
            "description": "Conflict - the deployment is being deleted and can no longer be changed"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update a deployment",
        "tags": [
          "Deployments"
        ]
      },
      "delete": {
        "description": "Withdraws a deployment and requests its removal from every targeted device. FM-844: this does NOT confirm removal — the device is the authority and reports `removing` -> `removed`/`failed` over the Margo status endpoint. Poll `GET /api/v1/deployments/:id/removal-status` to see which devices have actually confirmed.",
        "operationId": "deleteDeployment",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removal requested successfully; device confirmation is reported separately"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          },
          "409": {
            "description": "Conflict - the deployment is being deleted and can no longer be changed"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete a deployment",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/targets/remove": {
      "post": {
        "description": "Removes the deployment from the given devices (or from every current target in one label group) while it stays live on the others. The group is resolved once, at request time; devices that join it later are unaffected. A removed device stays excluded, even if it still matches the deployment selector, until it is re-included. Like DELETE /deployments/:id this only requests removal: the device confirms via the Margo status endpoint, and progress is visible in `removal-status` and `awaiting-removal`. When the request covers every remaining target, the whole deployment is removed and `deploymentRemoved` is true. Provide exactly one of `deviceIds` or `group`.",
        "operationId": "removeDeploymentTargets",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RemoveDeploymentTargetsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removal requested",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveDeploymentTargetsResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Neither or both of deviceIds/group given, the device was already removed, or the group has no current target"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found, or the device is not a target"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Remove a deployment from devices or a device group",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/targets/split": {
      "post": {
        "description": "Each given device becomes its own deployment with a copy of the configuration. The workload stays installed.",
        "operationId": "splitDeploymentTargets",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SplitDeploymentTargetsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Devices split off"
          },
          "400": {
            "description": "Invalid body or a device is not a target"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Split devices off a deployment",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/devices/{deviceId}": {
      "put": {
        "description": "Splits the device off its deployment (unless it is the only target) and applies the change to it alone. Returns the deployment that now holds the device.",
        "operationId": "updateDeploymentOnDevice",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "deviceId",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDeviceDeploymentDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deployment updated on the device"
          },
          "400": {
            "description": "Invalid body or the device is not a target"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update a deployment on one device",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/redeploy": {
      "post": {
        "description": "Moves the deployment to a new revision (application version and the complete set of parameter overrides) in place. The deployment id, the instance ids and the Margo ApplicationDeployment ids stay the same, so a device sees the same id with a new digest and updates without an uninstall. Allowed while the deployment is PENDING, INSTALLING, INSTALLED or FAILED, or its rollout was rolled back; refused (409) during a staged rollout and for removed deployments. A deployment group (multi-profile create) moves together in one transaction. The target version must belong to the same application, must not be retired or without a source registry, must declare every deployment profile in use and must be servable under Margo rc2. Parameters are validated like on create, and an immutable parameter of an installed application cannot change. A request that equals the current revision is refused (409). To re-deploy a single device, split it off first (POST /deployments/{id}/targets/split), then re-deploy the new deployment; PUT /deployments/{id}/devices/{deviceId} does not change the version.",
        "operationId": "redeployDeployment",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedeployDeploymentDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deployment moved to a new revision",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedeployDeploymentResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Unknown or missing body field, or parameter overrides that violate the application (PARAMETER_VALIDATION_FAILED, IMMUTABLE_PARAMETER_CHANGED)"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment or target version not found in the caller organization"
          },
          "409": {
            "description": "State does not allow a re-deploy, version retired or without source, identical content, retired parameter shape, or a concurrent change (code field)"
          },
          "422": {
            "description": "Target version of another application, a profile in use is missing, or rc2 admission would drop the revision (code field)"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Re-deploy a deployment with a new version and parameters",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/targets/include": {
      "post": {
        "description": "Makes devices that were removed from the deployment ordinary targets again. They return to the device desired state on its next poll.",
        "operationId": "includeDeploymentTargets",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IncludeDeploymentTargetsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Devices re-included",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncludeDeploymentTargetsResultDto"
                }
              }
            }
          },
          "400": {
            "description": "A device was not removed from this deployment"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found, or the device is not a target"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Re-include devices into a deployment",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/start": {
      "post": {
        "description": "Starts a deployment, triggering workload distribution to target devices.",
        "operationId": "startDeployment",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployment started successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Start a deployment",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/stop": {
      "post": {
        "description": "Stops a running deployment.",
        "operationId": "stopDeployment",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployment stopped successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Stop a deployment",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/removal-status": {
      "get": {
        "description": "Reports, per device, whether a deleted deployment has actually been removed from the client. Deleting a deployment only requests removal; the device confirms it by reporting `removed` (or `failed`) over the Margo status endpoint. Devices that have been asked and have not answered are reported as `awaiting`, which is neither a success nor a failure.",
        "operationId": "getDeploymentRemovalStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removal status retrieved successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get removal status",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/removal/confirm": {
      "post": {
        "description": "Marks the deployment as removed from the given devices although the devices did not confirm it. Use it when you know the workload is gone (for example the device was wiped or replaced) and waiting for the confirmation timeout helps nobody. Each device must have a removal request in force that is not yet confirmed: the deployment was deleted, or it was removed from that device. A device that reported `failed` may be confirmed too. The status history records the operator and the optional note. The deployment leaves `awaiting-removal` once no device is outstanding.",
        "operationId": "confirmDeploymentRemoval",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfirmDeploymentRemovalDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removal confirmed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfirmDeploymentRemovalResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, removal was not requested for a device, or its removal is already confirmed"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found, or the device is not a target"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Confirm a removal manually",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/rollout-status": {
      "get": {
        "description": "Retrieves the current rollout phase, batch progress and per-status instance counts.",
        "operationId": "getRolloutStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rollout status retrieved successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get rollout status",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/rollout/initialize": {
      "post": {
        "description": "Assigns deployment instances to batches (rolling) or to the canary cohort (canary).",
        "operationId": "initializeRollout",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rollout initialized successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Initialize a rollout",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/rollout/start": {
      "post": {
        "description": "Begins deploying the first batch (or canary cohort) of an initialized rollout.",
        "operationId": "startRollout",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rollout started successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Start a rollout",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/promote": {
      "post": {
        "description": "Promotes the canary cohort, progressing the rollout to the remaining devices.",
        "operationId": "promoteCanary",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Canary promoted successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Promote a canary rollout",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/approve-batch": {
      "post": {
        "description": "Approves the next wave for a rolling rollout configured for manual approval.",
        "operationId": "approveBatch",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Batch approved successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Approve the next rollout batch",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/pause": {
      "post": {
        "description": "Pauses the rollout at the current batch boundary.",
        "operationId": "pauseRollout",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rollout paused successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Pause a rollout",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/resume": {
      "post": {
        "description": "Resumes a paused rollout from its current phase.",
        "operationId": "resumeRollout",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rollout resumed successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Resume a rollout",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/deployments/{id}/rollback": {
      "post": {
        "description": "Aborts the in-flight rollout and marks it rolled back. This does not redeploy a previous application version. The effect depends on the deployment history. A rollout of the first revision withdraws the deployment from every device it was released to, and the devices uninstall it. A rollout that follows a re-deploy (POST /deployments/{id}/redeploy) does not withdraw anything: devices that already run the new revision move back to the revision they ran before, and devices that still wait for their batch keep running it. The deployment stays in every device manifest. A device that ran nothing before the re-deploy is withdrawn as for a first revision. A rolled-back deployment can be re-deployed again.",
        "operationId": "triggerRollback",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Deployment UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TriggerRollbackDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rollback triggered successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Deployment not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Abort an in-flight rollout",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/devices/rollback-version": {
      "post": {
        "description": "Re-deploys, in place, the revision (version and parameters) each matching device ran before its current one. Each device follows the rules of the device route: a device of a shared deployment is split off first, and its Margo deployment id is kept. Devices with no earlier revision, or with history written before revisions existed, are reported as skipped rather than failing the batch. This does not abort an in-flight rollout — see the deployment rollback route.",
        "operationId": "rollbackDevicesBySelector",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RollbackDevicesBySelectorDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rollback evaluated for every matching device"
          },
          "400": {
            "description": "deviceSelector missing or malformed"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "No devices matched the selector"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Roll back a group of devices to their previous application version",
        "tags": [
          "Devices"
        ]
      }
    },
    "/api/v1/devices/{id}/rollback-version": {
      "post": {
        "description": "Re-deploys, in place, the revision (version and parameters) this device ran before its current one, producing a new manifest version (never reusing or decrementing). The deployment id and the Margo deployment id stay the same: no deployment is created and no instance is deleted (the returned deploymentId is the existing one; since FM-1446 it is no longer a new \"Rollback to X\" deployment). A device that shares its deployment with other devices is split off first; the others stay on their revision, and deploymentId is then the fork. Returns 409 (code ROLLBACK_NO_PREVIOUS_REVISION) when no earlier revision differs, and 409 (code ROLLBACK_NO_REVISION_TARGET) when the device has only history written before revisions existed. This does not abort an in-flight rollout.",
        "operationId": "rollbackDeviceVersion",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RollbackDeviceVersionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Device rolled back to its previous version"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Device not found"
          },
          "409": {
            "description": "No earlier revision to roll back to (ROLLBACK_NO_PREVIOUS_REVISION), or only history without revisions (ROLLBACK_NO_REVISION_TARGET)"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Roll back a device to its previous application version",
        "tags": [
          "Devices"
        ]
      }
    },
    "/api/v1/devices/{id}/rollback-target": {
      "get": {
        "description": "Returns which revision the device would be rolled back to (version, parameter digest and revision number, plus whether the device is split off a shared deployment first), or a machine-readable code explaining why it cannot be. Read-only — nothing is deployed. The resolution rule lives on the server so clients need not re-derive it.",
        "operationId": "getDeviceRollbackTarget",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "applicationPackageId",
            "required": false,
            "in": "query",
            "description": "Scope the preview to one application package. Mirrors the rollback body so a preview cannot name a version the rollback would not apply.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rollback target resolved (or reported unavailable)"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Resolve the rollback target for a device",
        "tags": [
          "Devices"
        ]
      }
    },
    "/api/v1/devices/{id}/version-history": {
      "get": {
        "description": "Recorded deployment version lineage for the device, newest first. Backs the confirm dialog that names both versions before a rollback.",
        "operationId": "getDeviceVersionHistory",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Version history retrieved"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get a device deployment version history",
        "tags": [
          "Devices"
        ]
      }
    },
    "/api/v1/deployment-groups/{groupId}/redeploy": {
      "post": {
        "description": "Moves every live member of a deployment group (the deployments of one multi-profile create) to a new revision in one transaction: all members or none. The body is the same as for POST /deployments/{id}/redeploy: the target application version and ONE complete set of parameter overrides for the whole group (per-profile overrides are not supported). Every member must allow a re-deploy and the target version must declare every deployment profile in use; a refusal names the offending member ids (deploymentIds). A member that already runs the requested content is left as it is; the request is refused (409) only when every member already runs it. An unknown, foreign or empty group answers 404 REDEPLOY_GROUP_NOT_FOUND.",
        "operationId": "redeployDeploymentGroup",
        "parameters": [
          {
            "name": "groupId",
            "required": true,
            "in": "path",
            "description": "Deployment group UUID (deploymentGroupId)",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedeployDeploymentDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The group moved to a new revision",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedeployDeploymentResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Unknown or missing body field, a group id that is not a UUID, or parameter overrides that violate the application"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Group (REDEPLOY_GROUP_NOT_FOUND) or target version not found in the caller organization"
          },
          "409": {
            "description": "A member state does not allow a re-deploy, version retired or without source, every member identical, or a concurrent change (code, deploymentIds)"
          },
          "422": {
            "description": "Target version of another application, a profile in use is missing, or rc2 admission would drop the revision (code, deploymentIds)"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Re-deploy a deployment group with a new version and parameters",
        "tags": [
          "Deployments"
        ]
      }
    },
    "/api/v1/groups/stats": {
      "get": {
        "description": "Retrieves fleet-wide statistics including device counts and available label keys.",
        "operationId": "getFleetStats",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fleet statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FleetStatsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get fleet statistics",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/labels": {
      "get": {
        "description": "Retrieves all unique label keys used in the organization.",
        "operationId": "getLabelKeys",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Label keys retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LabelKeysResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get label keys",
        "tags": [
          "Groups"
        ]
      },
      "patch": {
        "description": "Bulk updates labels on devices matching a selector.",
        "operationId": "bulkUpdateLabels",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkUpdateLabelsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Labels updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkOperationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Bulk update labels",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/labels/{key}/values": {
      "get": {
        "description": "Retrieves all unique values for a specific label key.",
        "operationId": "getLabelValues",
        "parameters": [
          {
            "name": "key",
            "required": true,
            "in": "path",
            "description": "Label key",
            "schema": {
              "example": "environment",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Label values retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LabelValuesResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get label values",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/by-label/{key}": {
      "get": {
        "description": "Retrieves device statistics grouped by a specific label key.",
        "operationId": "getStatsByLabel",
        "parameters": [
          {
            "name": "key",
            "required": true,
            "in": "path",
            "description": "Label key to group by",
            "schema": {
              "example": "environment",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupStatsByLabelResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get statistics by label",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/query": {
      "post": {
        "description": "Retrieves statistics for devices matching a label selector.",
        "operationId": "queryGroupStats",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryGroupStatsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Group statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupStatsResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "labelSelector is missing, empty or not a valid label selector"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Query group statistics",
        "tags": [
          "Groups"
        ]
      },
      "get": {
        "description": "Retrieves statistics for devices matching label query parameters. Pass label key-value pairs as query parameters (e.g., ?env=prod&region=us-east). Reserved keys `limit` and `offset` are excluded from label matching and must be non-negative integers. The pairs must form a valid label selector: 1-20 pairs, one value per key, keys 1-253 and values up to 253 characters, no control characters. An empty selector is rejected with 400; use GET /api/v1/groups/stats for the whole fleet.",
        "operationId": "getGroupStats",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Group statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupStatsResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "The query parameters do not form a valid, non-empty label selector"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get group statistics via query params",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/labels/add": {
      "post": {
        "description": "Adds a label to all devices matching a selector.",
        "operationId": "addLabelToGroup",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddLabelToGroupDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Label added successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkOperationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Add label to group",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/labels/remove": {
      "post": {
        "description": "Removes a label from all devices matching a selector.",
        "operationId": "removeLabelFromGroup",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RemoveLabelFromGroupDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Label removed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkOperationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Remove label from group",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/labels/{key}": {
      "patch": {
        "description": "Renames a label key on every device of the organization in one transaction. Deployment selectors and alert rules that reference the key are rewritten in the same transaction, so they keep targeting the same devices. Renaming onto a key that devices already carry, or that a selector or alert rule already references, fails with 409 and changes nothing.",
        "operationId": "renameLabelKey",
        "parameters": [
          {
            "name": "key",
            "required": true,
            "in": "path",
            "description": "The label key to rename",
            "schema": {
              "example": "plant",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenameLabelKeyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Label key renamed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LabelRenameResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid key or new key"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "No device in the organization carries the key"
          },
          "409": {
            "description": "The new key is already in use"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Rename a label key",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/labels/{key}/values/{value}": {
      "patch": {
        "description": "Renames one value of a label key (for example site=Plant-A to site=Plant-1) on every device of the organization in one transaction, rewriting the deployment selectors and alert rules that select it. Renaming onto a value already in use fails with 409.",
        "operationId": "renameLabelValue",
        "parameters": [
          {
            "name": "value",
            "required": true,
            "in": "path",
            "description": "The value to rename",
            "schema": {
              "example": "Plant-A",
              "type": "string"
            }
          },
          {
            "name": "key",
            "required": true,
            "in": "path",
            "description": "The label key",
            "schema": {
              "example": "site",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenameLabelValueDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Label value renamed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LabelRenameResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid key, value or new value"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "No device in the organization carries the label"
          },
          "409": {
            "description": "The new value is already in use"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Rename a label value",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/groups/devices": {
      "delete": {
        "description": "Soft deletes devices matching a selector.",
        "operationId": "bulkDeleteGroup",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkDeleteGroupDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Devices deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkDeleteResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Bulk delete devices",
        "tags": [
          "Groups"
        ]
      }
    },
    "/api/v1/device-groups": {
      "post": {
        "description": "Creates an empty device group. The name is unique (case-insensitive).",
        "operationId": "createDeviceGroup",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDeviceGroupDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceGroupResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "409": {
            "description": "A live group with this name exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create a device group",
        "tags": [
          "Device Groups"
        ]
      },
      "get": {
        "description": "Paginated list of device groups with member counts, in name order. With `deviceId`, only the groups that device is a member of.",
        "operationId": "listDeviceGroups",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "allOf": [
                {
                  "$ref": "#/components/schemas/Object"
                }
              ]
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 0,
              "default": 0,
              "allOf": [
                {
                  "$ref": "#/components/schemas/Object"
                }
              ]
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Case-insensitive substring search",
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "name": "deviceId",
            "required": false,
            "in": "query",
            "description": "Only the groups this device is a member of. 404 when the device is not a live device of the organization.",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "hasMembers",
            "required": false,
            "in": "query",
            "description": "true: only groups with at least one live member device. false: only empty groups. Any other value is rejected with 400.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceGroupListResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "deviceId is not a live device of the organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List device groups",
        "tags": [
          "Device Groups"
        ]
      }
    },
    "/api/v1/device-groups/{id}": {
      "get": {
        "operationId": "getDeviceGroup",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device group UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceGroupResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Group not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get a device group",
        "tags": [
          "Device Groups"
        ]
      },
      "patch": {
        "description": "Renames the group or changes its description.",
        "operationId": "updateDeviceGroup",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device group UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDeviceGroupDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceGroupResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Group not found"
          },
          "409": {
            "description": "A live group with this name exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update a device group",
        "tags": [
          "Device Groups"
        ]
      },
      "delete": {
        "description": "Soft-deletes the group and its memberships. Deployments that targeted it are not split or removed.",
        "operationId": "deleteDeviceGroup",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device group UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Group deleted"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Group not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete a device group",
        "tags": [
          "Device Groups"
        ]
      }
    },
    "/api/v1/device-groups/{id}/members": {
      "get": {
        "description": "Paginated live member devices. `search` matches the device search text.",
        "operationId": "listDeviceGroupMembers",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device group UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "allOf": [
                {
                  "$ref": "#/components/schemas/Object"
                }
              ]
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 0,
              "default": 0,
              "allOf": [
                {
                  "$ref": "#/components/schemas/Object"
                }
              ]
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Case-insensitive substring search",
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceGroupMemberListResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Group not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List the members of a device group",
        "tags": [
          "Device Groups"
        ]
      },
      "post": {
        "description": "Idempotent: devices that are members already are counted in `alreadyMembers`. Refused as a whole with 400 when any device is not a live device of the organization.",
        "operationId": "addDeviceGroupMembers",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device group UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceGroupMembersDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddDeviceGroupMembersResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body or unknown device"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Group not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Add devices to a device group",
        "tags": [
          "Device Groups"
        ]
      },
      "delete": {
        "description": "Idempotent: devices that are not members are listed in `notMemberDeviceIds`. Deployments that reached a removed device only through this group are split off in the same transaction; the workload stays installed.",
        "operationId": "removeDeviceGroupMembers",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device group UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceGroupMembersDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveDeviceGroupMembersResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Group not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Remove devices from a device group",
        "tags": [
          "Device Groups"
        ]
      }
    },
    "/api/v1/organizations": {
      "post": {
        "description": "Creates a new organization. Requires Super Admin privileges.",
        "operationId": "createOrganization",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrganizationDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Organization created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "409": {
            "description": "Organization with this slug already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create organization",
        "tags": [
          "Organizations"
        ]
      },
      "get": {
        "description": "Retrieves a list of all organizations. Requires Super Admin privileges.",
        "operationId": "listOrganizations",
        "parameters": [
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search term for organization name or slug",
            "schema": {
              "example": "acme",
              "type": "string"
            }
          },
          {
            "name": "name",
            "required": false,
            "in": "query",
            "description": "Filter by exact organization name",
            "schema": {
              "example": "Acme Corporation",
              "type": "string"
            }
          },
          {
            "name": "active",
            "required": false,
            "in": "query",
            "description": "Filter by active status (deprecated, use status filter)",
            "deprecated": true,
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by organization status",
            "schema": {
              "example": "active",
              "type": "string",
              "enum": [
                "active",
                "suspended",
                "pending",
                "disabled"
              ]
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of results to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of results to skip for pagination",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organizations retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List organizations",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/stats": {
      "get": {
        "description": "Returns total organization count and counts by status. Requires Super Admin privileges.",
        "operationId": "getOrganizationAggregateStats",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregate stats retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationAggregateStatsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get aggregate organization stats",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/empty": {
      "get": {
        "description": "Returns organizations with zero members. The organization the caller is currently operating in (active tenant) is never included, so it can never be purged. Requires Super Admin privileges.",
        "operationId": "getEmptyOrganizations",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Empty organizations retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OrganizationResponseDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get empty organizations",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/bulk": {
      "delete": {
        "description": "Purges multiple organizations, cascading through each org's devices, deployments and applications (soft-deleted) before removing the org itself. The organization the caller is currently operating in (active tenant) is never deleted — it is dropped from the request and reported in the `errors` array as skipped. The response reports how many organizations were purged, the aggregated cascade counts, and a per-organization error for any that could not be removed. Requires Super Admin privileges.",
        "operationId": "bulkDeleteOrganizations",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bulk delete completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkDeleteOrganizationsResultDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Bulk delete organizations",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}": {
      "get": {
        "description": "Retrieves an organization by its unique identifier.",
        "operationId": "getOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organization retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get organization by ID",
        "tags": [
          "Organizations"
        ]
      },
      "patch": {
        "description": "Updates an existing organization. Requires Super Admin privileges.",
        "operationId": "updateOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateOrganizationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Organization updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update organization",
        "tags": [
          "Organizations"
        ]
      },
      "delete": {
        "description": "Deletes an organization. When `force=true`, performs cascading deletion of all users, devices, deployments, and applications. Multi-tenant users keep their account and lose membership in this org only; single-tenant users are hard-deleted. Requires Super Admin privileges.",
        "operationId": "deleteOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "force",
            "required": false,
            "in": "query",
            "description": "When set to `true`, performs cascading deletion across users, devices, deployments, and applications. Optional.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organization force-deleted with cascade summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForceDeleteResultDto"
                }
              }
            }
          },
          "204": {
            "description": "Organization deleted successfully (no force)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          },
          "409": {
            "description": "Organization has active resources or is your own"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/by-slug/{slug}": {
      "get": {
        "description": "Retrieves an organization by its URL-friendly slug.",
        "operationId": "getOrganizationBySlug",
        "parameters": [
          {
            "name": "slug",
            "required": true,
            "in": "path",
            "description": "Organization slug",
            "schema": {
              "example": "acme-corp",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organization retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get organization by slug",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}/stats": {
      "get": {
        "description": "Retrieves an organization with its statistics (user count, device count, etc.).",
        "operationId": "getOrganizationWithStats",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organization with stats retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationWithStatsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get organization with stats",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}/enable": {
      "post": {
        "description": "Enables a disabled organization. Requires Super Admin privileges.",
        "operationId": "enableOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnableOrganizationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Organization enabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Organization is already enabled"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Enable organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}/disable": {
      "post": {
        "description": "Disables an active organization. Requires Super Admin privileges.",
        "operationId": "disableOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DisableOrganizationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Organization disabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Organization is already disabled"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Disable organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}/suspend": {
      "post": {
        "description": "Temporarily suspends an organization. Different from disable - intended for reversible suspension. Requires Super Admin privileges.",
        "operationId": "suspendOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SuspendOrganizationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Organization suspended successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Organization is already suspended or disabled"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Suspend organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}/rename": {
      "post": {
        "description": "Renames an organization with new name and optional slug. Requires Super Admin privileges.",
        "operationId": "renameOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenameOrganizationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Organization renamed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          },
          "409": {
            "description": "Slug already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Rename organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}/deletion-check": {
      "get": {
        "description": "Returns blockers preventing deletion. Requires Super Admin privileges.",
        "operationId": "checkOrganizationDeletion",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deletion check completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletionCheckResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Check if organization can be deleted",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}/bulk-users": {
      "post": {
        "description": "Perform bulk operations on users within an organization. Requires Super Admin privileges.",
        "operationId": "organizationBulkUserOperation",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrganizationBulkOperationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bulk operation completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationBulkOperationResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Bulk user operations",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/organizations/{id}/users/{userId}/reset-password": {
      "post": {
        "description": "Triggers a password reset for a user that is a member of this organization. Allowed for tenant admins (USERS_WRITE) and superadmins. Refuses self-reset (use the profile security page instead) and reset for inactive users.\n\n`delivery: 'email'` (the default) is NON-DESTRUCTIVE: the existing password keeps working and the user receives a Keycloak `UPDATE_PASSWORD` email carrying the reset form — only the user sets the new password. Delivery preconditions are checked before anything is mutated, so a `409 no-delivery-channel` means nothing was changed.\n\n`delivery: 'display'` is the documented no-SMTP / air-gap recovery path only: it rotates the credential and returns `temporaryPassword` exactly once.",
        "operationId": "resetUserPasswordInOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResetPasswordDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password reset triggered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PasswordResetResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Self-reset attempted or user is not active"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks USERS_WRITE in this org"
          },
          "404": {
            "description": "User is not a member of this organization"
          },
          "409": {
            "description": "no-delivery-channel — delivery='email' was requested but the user has no linked Keycloak account or the realm has no SMTP server. Nothing was changed."
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Reset password for a user in this organization",
        "tags": [
          "Organizations"
        ]
      }
    },
    "/api/v1/users": {
      "post": {
        "description": "Creates a new user. Requires Super Admin privileges.",
        "operationId": "createUser",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "User created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "409": {
            "description": "User with this email already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create user",
        "tags": [
          "Users"
        ]
      },
      "get": {
        "description": "Retrieves a list of all users. Requires Super Admin privileges.",
        "operationId": "listUsers",
        "parameters": [
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search term for user name or email",
            "schema": {
              "example": "john",
              "type": "string"
            }
          },
          {
            "name": "email",
            "required": false,
            "in": "query",
            "description": "Filter by exact email address",
            "schema": {
              "format": "email",
              "example": "john.doe@example.com",
              "type": "string"
            }
          },
          {
            "name": "active",
            "required": false,
            "in": "query",
            "description": "Filter by active status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "isSuperAdmin",
            "required": false,
            "in": "query",
            "description": "Filter by super admin status",
            "schema": {
              "example": false,
              "type": "boolean"
            }
          },
          {
            "name": "include",
            "required": false,
            "in": "query",
            "description": "Include related data. Use \"organizations\" to include organization memberships.",
            "schema": {
              "example": "organizations",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of results to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of results to skip for pagination",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Users retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List users",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/stats": {
      "get": {
        "description": "Returns platform-wide user stats: total, byStatus (active/inactive), byRole keyed by role name, and pendingInvitations. Requires Super Admin privileges.",
        "operationId": "getUserStats",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "User stats retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserAggregateStatsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get aggregate user stats",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}": {
      "get": {
        "description": "Retrieves a user with their organization memberships. Requires Super Admin privileges.",
        "operationId": "getUser",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "User retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserWithOrganizationsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get user by ID",
        "tags": [
          "Users"
        ]
      },
      "put": {
        "description": "Updates an existing user. Requires Super Admin privileges.",
        "operationId": "updateUser",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUserDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update user",
        "tags": [
          "Users"
        ]
      },
      "delete": {
        "description": "Deletes a user. Requires Super Admin privileges.",
        "operationId": "deleteUser",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "User deleted successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete user",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/organizations": {
      "post": {
        "description": "Adds a user to an organization with a specific role. Requires Super Admin privileges.",
        "operationId": "addUserToOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddUserToOrganizationDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "User added to organization successfully"
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User or organization not found"
          },
          "409": {
            "description": "User already belongs to this organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Add user to organization",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/organizations/{organizationId}": {
      "delete": {
        "description": "Removes a user from an organization. Requires Super Admin privileges.",
        "operationId": "removeUserFromOrganization",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "User removed from organization successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User or organization membership not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Remove user from organization",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/resend-verification": {
      "post": {
        "description": "Asks Keycloak to re-send the verification e-mail for a user whose address is not yet verified. Returns 400 when the address is already verified, because sending would imply the account is blocked when it is not.",
        "operationId": "resendUserVerificationEmail",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResendVerificationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "The address is already verified"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "User not found, not a member of the active organization, or has no Keycloak account"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Resend the e-mail verification message",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/keycloak-link": {
      "delete": {
        "description": "Unbinds users.keycloakId so the account can be re-linked on next sign-in, subject to the FM-837 checks. Use when a Keycloak account was legitimately re-created. Memberships and system roles are preserved. Requires Super Admin privileges.",
        "operationId": "clearUserKeycloakLink",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Link cleared"
          },
          "400": {
            "description": "Malformed id, the user has no Keycloak link to clear, or the target is the caller (whose link is live by definition)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Clear the link between a user and their Keycloak identity",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/organizations/{organizationId}/role": {
      "put": {
        "description": "Updates a user's role in an organization. Requires USERS_WRITE or ORG_WRITE permission. Cannot change own role or escalate beyond own role level.",
        "operationId": "updateUserOrganizationRole",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUserRoleDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "User role updated successfully"
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Cannot change own role or escalate privileges"
          },
          "404": {
            "description": "User, organization, or role not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update user role in organization",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/enable": {
      "post": {
        "description": "Enables a disabled user account. Requires Super Admin privileges.",
        "operationId": "enableUser",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnableUserDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User enabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "User is already enabled"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Enable user",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/disable": {
      "post": {
        "description": "Disables a user account. Requires Super Admin privileges. Cannot disable yourself or other superusers unless you are a superuser.",
        "operationId": "disableUser",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DisableUserDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User disabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "User is already disabled"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Cannot disable yourself or superusers"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Disable user",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/reset-password": {
      "post": {
        "description": "Triggers a password reset for a user. Requires Super Admin privileges.\n\n`delivery: 'email'` (the default) is NON-DESTRUCTIVE: the user's existing password keeps working, Keycloak records an `UPDATE_PASSWORD` required action and emails them the reset form. Only the user can set the new password. Every delivery precondition is checked before anything is mutated, so a `409 no-delivery-channel` means nothing was changed.\n\n`delivery: 'display'` is the documented no-SMTP / air-gap recovery path only. It rotates the credential to a generated value, marks it temporary in Keycloak and returns it in `temporaryPassword` exactly once — it cannot be retrieved again.",
        "operationId": "resetUserPassword",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResetPasswordDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password reset successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PasswordResetResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          },
          "409": {
            "description": "no-delivery-channel — delivery='email' was requested but the user has no linked Keycloak account or the realm has no SMTP server. Nothing was changed."
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Reset user password",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/grant-superuser": {
      "post": {
        "description": "Grants superuser (system admin) role to a user. Requires Super Admin privileges.",
        "operationId": "grantSuperuser",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Superuser role granted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Grant superuser role",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/{id}/revoke-superuser": {
      "post": {
        "description": "Revokes superuser (system admin) role from a user. Requires Super Admin privileges.",
        "operationId": "revokeSuperuser",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Superuser role revoked successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "User does not have superuser role"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Revoke superuser role",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/bulk": {
      "post": {
        "description": "Performs a bulk operation on multiple users. Supported actions: enable, disable, delete, reset_password, assign_role. Requires Super Admin privileges.",
        "operationId": "bulkUserOperation",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkOperationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bulk operation completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkOperationResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Bulk user operation",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/users/export": {
      "post": {
        "description": "Exports users to CSV or JSON format. Requires Super Admin privileges.",
        "operationId": "exportUsers",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExportUsersDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Users exported successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Export users",
        "tags": [
          "Users"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/members/stats": {
      "get": {
        "description": "Returns aggregate stats for members of the organization: total, byStatus (active/inactive), byRole keyed by role name, and pendingInvitations.",
        "operationId": "getOrganizationMemberStats",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stats retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserAggregateStatsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get organization member stats",
        "tags": [
          "Organization Members"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/members": {
      "get": {
        "description": "Retrieves a list of members in the organization.",
        "operationId": "listOrganizationMembers",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search term for user name or email",
            "schema": {
              "example": "john",
              "type": "string"
            }
          },
          {
            "name": "email",
            "required": false,
            "in": "query",
            "description": "Filter by exact email address",
            "schema": {
              "format": "email",
              "example": "john.doe@example.com",
              "type": "string"
            }
          },
          {
            "name": "active",
            "required": false,
            "in": "query",
            "description": "Filter by active status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "isSuperAdmin",
            "required": false,
            "in": "query",
            "description": "Filter by super admin status",
            "schema": {
              "example": false,
              "type": "boolean"
            }
          },
          {
            "name": "include",
            "required": false,
            "in": "query",
            "description": "Include related data. Use \"organizations\" to include organization memberships.",
            "schema": {
              "example": "organizations",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of results to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of results to skip for pagination",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Members retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List organization members",
        "tags": [
          "Organization Members"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/members/{userId}": {
      "get": {
        "description": "Retrieves details of a specific organization member.",
        "operationId": "getOrganizationMember",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Member retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserWithOrganizationsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "User not found in this organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get organization member",
        "tags": [
          "Organization Members"
        ]
      },
      "delete": {
        "description": "Removes a member from the organization.",
        "operationId": "removeOrganizationMember",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Member removed successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "User not found in this organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Remove member from organization",
        "tags": [
          "Organization Members"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/members/{userId}/role": {
      "put": {
        "description": "Updates a member's role in the organization.",
        "operationId": "updateOrganizationMemberRole",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUserRoleDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Member role updated successfully"
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Member or role not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update member role",
        "tags": [
          "Organization Members"
        ]
      }
    },
    "/api/v1/tenant/members/stats": {
      "get": {
        "description": "Returns aggregate stats for members of the active organization (resolved from URL slug / active switch): total, byStatus (active/inactive), byRole keyed by role name, and pendingInvitations.",
        "operationId": "getTenantMemberStats",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stats retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserAggregateStatsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get active-tenant member stats",
        "tags": [
          "Tenant Members"
        ]
      }
    },
    "/api/v1/tenant/members": {
      "get": {
        "description": "Retrieves a list of members in the active organization (resolved from URL slug / active switch).",
        "operationId": "listTenantMembers",
        "parameters": [
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search term for user name or email",
            "schema": {
              "example": "john",
              "type": "string"
            }
          },
          {
            "name": "email",
            "required": false,
            "in": "query",
            "description": "Filter by exact email address",
            "schema": {
              "format": "email",
              "example": "john.doe@example.com",
              "type": "string"
            }
          },
          {
            "name": "active",
            "required": false,
            "in": "query",
            "description": "Filter by active status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "isSuperAdmin",
            "required": false,
            "in": "query",
            "description": "Filter by super admin status",
            "schema": {
              "example": false,
              "type": "boolean"
            }
          },
          {
            "name": "include",
            "required": false,
            "in": "query",
            "description": "Include related data. Use \"organizations\" to include organization memberships.",
            "schema": {
              "example": "organizations",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of results to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of results to skip for pagination",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Members retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List active-tenant members",
        "tags": [
          "Tenant Members"
        ]
      }
    },
    "/api/v1/me/profile": {
      "get": {
        "operationId": "UserProfileController_getMyProfile",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserProfileDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get current user profile",
        "tags": [
          "Profile"
        ]
      },
      "put": {
        "operationId": "UserProfileController_updateMyProfile",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProfileDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserProfileDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update current user profile",
        "tags": [
          "Profile"
        ]
      }
    },
    "/api/v1/me/profile/notifications": {
      "put": {
        "operationId": "UserProfileController_updateNotificationPreferences",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateNotificationPreferencesDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserProfileDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update notification preferences",
        "tags": [
          "Profile"
        ]
      }
    },
    "/api/v1/me/password": {
      "post": {
        "operationId": "UserProfileController_changePassword",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangePasswordDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Password changed successfully"
          },
          "400": {
            "description": "Invalid current password or weak new password"
          },
          "409": {
            "description": "FM-864: the account is not linked to an identity provider account, so it has no password to change"
          },
          "502": {
            "description": "FM-864: the identity provider rejected the new password; nothing was changed"
          },
          "503": {
            "description": "FM-864: the identity provider is unavailable, so the password was not changed"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Change password",
        "tags": [
          "Profile"
        ]
      }
    },
    "/api/v1/me/sessions": {
      "get": {
        "operationId": "UserProfileController_getMySessions",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionListResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List active sessions",
        "tags": [
          "Profile"
        ]
      },
      "delete": {
        "operationId": "UserProfileController_terminateAllOtherSessions",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Number of sessions terminated"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Terminate all other sessions",
        "tags": [
          "Profile"
        ]
      }
    },
    "/api/v1/me/sessions/{sessionId}": {
      "delete": {
        "operationId": "UserProfileController_terminateSession",
        "parameters": [
          {
            "name": "sessionId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Session terminated"
          },
          "400": {
            "description": "Cannot terminate current session"
          },
          "404": {
            "description": "Session not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Terminate a session",
        "tags": [
          "Profile"
        ]
      }
    },
    "/api/v1/me/activity": {
      "get": {
        "operationId": "UserProfileController_getMyActivity",
        "parameters": [
          {
            "name": "activityTypes",
            "required": false,
            "in": "query",
            "description": "Filter by activity types",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Filter by start date",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "Filter by end date",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "success",
            "required": false,
            "in": "query",
            "description": "Filter by success status",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Offset for pagination",
            "schema": {
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActivityListResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get my activity history",
        "tags": [
          "Profile"
        ]
      }
    },
    "/api/v1/tenant-consistency/users": {
      "get": {
        "description": "Compares each user’s database organization memberships with the SET of values of the Keycloak organization_id attribute (order is ignored: Keycloak does not preserve it). Each row lists the organizations missing in and extra in Keycloak. Without a classification filter, consistent users are not listed. Requires Super Admin privileges.",
        "operationId": "listTenantConsistency",
        "parameters": [
          {
            "name": "classification",
            "required": false,
            "in": "query",
            "description": "Only list users with this classification. Omit to list every inconsistent user (everything except \"consistent\").",
            "schema": {
              "type": "string",
              "enum": [
                "consistent",
                "hanging",
                "unassigned",
                "keycloak-attribute-names-unknown-org",
                "keycloak-only"
              ]
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantConsistencyListResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "503": {
            "description": "Keycloak is not configured"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List users whose tenant anchors disagree",
        "tags": [
          "Tenant consistency"
        ]
      }
    },
    "/api/v1/tenant-consistency/users/{userId}/anchor": {
      "post": {
        "description": "Makes the organization the user’s primary membership and sets the Keycloak organization_id attribute to exactly the user’s membership set, atomically with a rollback of Keycloak on failure. Creates the membership (default role viewer) when the user is not a member yet. A user without a live Keycloak account gets the database anchor only, and a link to a deleted Keycloak account is cleared. Requires Super Admin privileges.",
        "operationId": "anchorUserTenant",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnchorTenantDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnchorTenantResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User, organization or role not found"
          },
          "409": {
            "description": "Organization not active"
          },
          "500": {
            "description": "Rollback of Keycloak failed; the user must be checked manually"
          },
          "502": {
            "description": "Keycloak rejected the update or could not be read; nothing changed"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Anchor a user to one organization in logiccloud orchestrate and Keycloak",
        "tags": [
          "Tenant consistency"
        ]
      }
    },
    "/api/v1/tenant-consistency/users/{userId}": {
      "delete": {
        "description": "Deletes the linked Keycloak account, then the logiccloud orchestrate user row. Unlike DELETE /users/{id}, a Keycloak failure stops the delete (502) instead of leaving a Keycloak account behind. You cannot delete yourself. Requires Super Admin privileges.",
        "operationId": "deleteTenantConsistencyUser",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "User deleted in both stores"
          },
          "400": {
            "description": "Invalid id, or the caller’s own account"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          },
          "502": {
            "description": "Keycloak could not be read or did not delete"
          },
          "503": {
            "description": "Keycloak is not configured"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete a user completely, in Keycloak and logiccloud orchestrate",
        "tags": [
          "Tenant consistency"
        ]
      }
    },
    "/api/v1/tenant-consistency/keycloak-users/{keycloakId}/anchor": {
      "post": {
        "description": "For a Keycloak account with no logiccloud orchestrate user row (\"keycloak-only\"): creates the row from the Keycloak account and anchors it like POST /users/{userId}/anchor, in one transaction. Refused while Entra federation is enabled (user creation is governed by Entra). Requires Super Admin privileges.",
        "operationId": "anchorKeycloakOnlyAccount",
        "parameters": [
          {
            "name": "keycloakId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnchorTenantDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnchorTenantResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "403": {
            "description": "Forbidden - Super Admin required, or Entra enabled"
          },
          "404": {
            "description": "Keycloak account, organization or role not found"
          },
          "409": {
            "description": "The account already has a user, has no email, a user with its email exists, or the organization is not active"
          },
          "502": {
            "description": "Keycloak could not be read or rejected the update"
          },
          "503": {
            "description": "Keycloak is not configured"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create the user for a Keycloak-only account and anchor it to one organization",
        "tags": [
          "Tenant consistency"
        ]
      }
    },
    "/api/v1/tenant-consistency/keycloak-users/{keycloakId}": {
      "delete": {
        "description": "Deletes a Keycloak account that has no logiccloud orchestrate user row. An account that belongs to a user is refused (409); delete the user instead. Requires Super Admin privileges.",
        "operationId": "deleteKeycloakOnlyAccount",
        "parameters": [
          {
            "name": "keycloakId",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Keycloak account deleted"
          },
          "400": {
            "description": "Invalid id"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Keycloak account not found"
          },
          "409": {
            "description": "The account belongs to a logiccloud orchestrate user"
          },
          "502": {
            "description": "Keycloak could not be read or did not delete"
          },
          "503": {
            "description": "Keycloak is not configured"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete a Keycloak-only account",
        "tags": [
          "Tenant consistency"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/api-keys": {
      "post": {
        "description": "Creates a new API key for programmatic access. The full key is only returned once on creation.",
        "operationId": "createApiKey",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created successfully. Store the key securely.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreatedResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions\n\nForbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create API key",
        "tags": [
          "API Keys"
        ]
      },
      "get": {
        "description": "Retrieves all API keys for the organization with pagination.",
        "operationId": "listApiKeys",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by API key name",
            "schema": {
              "example": "production",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of results to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of results to skip for pagination",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "API keys retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List API keys",
        "tags": [
          "API Keys"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/api-keys/{id}": {
      "get": {
        "description": "Retrieves a specific API key by its unique identifier.",
        "operationId": "getApiKey",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "API Key UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "API key retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "API key not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get API key by ID",
        "tags": [
          "API Keys"
        ]
      },
      "put": {
        "description": "Updates an existing API key name, description, or permissions.",
        "operationId": "updateApiKey",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "API Key UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateApiKeyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "API key updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "API key not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update API key",
        "tags": [
          "API Keys"
        ]
      },
      "delete": {
        "description": "Revokes an API key, immediately invalidating it for all future requests.",
        "operationId": "revokeApiKey",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "API Key UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "API key revoked successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "API key not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Revoke API key",
        "tags": [
          "API Keys"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/invitations": {
      "post": {
        "description": "Sends an invitation email to a user to join the organization.",
        "operationId": "createInvitation",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInvitationDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invitation created and email sent successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "409": {
            "description": "User already in organization or invitation pending"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create invitation",
        "tags": [
          "Invitations"
        ]
      },
      "get": {
        "description": "Retrieves all invitations for the organization.",
        "operationId": "listInvitations",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "email",
            "required": false,
            "in": "query",
            "description": "Filter by email address",
            "schema": {
              "format": "email",
              "example": "user@example.com",
              "type": "string"
            }
          },
          {
            "name": "pending",
            "required": false,
            "in": "query",
            "description": "Filter for pending invitations only",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "expired",
            "required": false,
            "in": "query",
            "description": "Filter for expired invitations only",
            "schema": {
              "example": false,
              "type": "boolean"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invitations retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List invitations",
        "tags": [
          "Invitations"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/invitations/{id}": {
      "get": {
        "description": "Retrieves a specific invitation by its unique identifier.",
        "operationId": "getInvitation",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Invitation UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invitation retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Invitation not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get invitation by ID",
        "tags": [
          "Invitations"
        ]
      },
      "delete": {
        "description": "Deletes an invitation, preventing it from being accepted.",
        "operationId": "deleteInvitation",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Invitation UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Invitation deleted successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Invitation not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete invitation",
        "tags": [
          "Invitations"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/invitations/{id}/resend": {
      "post": {
        "description": "Resends an invitation email and optionally resets the expiration.",
        "operationId": "resendInvitation",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Invitation UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResendInvitationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Invitation resent successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invitation already accepted"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Invitation not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Resend invitation",
        "tags": [
          "Invitations"
        ]
      }
    },
    "/api/v1/invitations/{token}": {
      "get": {
        "description": "Retrieves invitation details using the token from the invitation email. No authentication required.",
        "operationId": "getInvitationByToken",
        "parameters": [
          {
            "name": "token",
            "required": true,
            "in": "path",
            "description": "Invitation token from the email",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invitation details retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvitationDetailsDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired token"
          },
          "404": {
            "description": "Invitation not found"
          }
        },
        "summary": "Get invitation details by token",
        "tags": [
          "Public Invitations"
        ]
      }
    },
    "/api/v1/invitations/accept": {
      "post": {
        "description": "Accepts an invitation and adds the user to the organization. If the user does not exist, name and password are required.",
        "operationId": "acceptInvitation",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcceptInvitationDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Invitation accepted successfully"
          },
          "400": {
            "description": "Invalid token, expired invitation, or missing required fields"
          },
          "404": {
            "description": "Invitation not found"
          },
          "409": {
            "description": "Invitation already accepted"
          }
        },
        "summary": "Accept invitation",
        "tags": [
          "Public Invitations"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/roles/permissions": {
      "get": {
        "description": "Retrieves all available permissions for role creation.",
        "operationId": "listPermissions",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Permissions retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List available permissions",
        "tags": [
          "Roles"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/roles": {
      "get": {
        "description": "Retrieves all roles available in the organization (system + custom roles).",
        "operationId": "listRoles",
        "parameters": [
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search term for role name",
            "schema": {
              "example": "admin",
              "type": "string"
            }
          },
          {
            "name": "includeSystem",
            "required": false,
            "in": "query",
            "description": "Whether to include system-defined roles",
            "schema": {
              "default": true,
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of results to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of results to skip for pagination",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Roles retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoleListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List roles",
        "tags": [
          "Roles"
        ]
      },
      "post": {
        "description": "Creates a new custom role for the organization.",
        "operationId": "createRole",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRoleDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Role created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoleResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "409": {
            "description": "Role with this name already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create custom role",
        "tags": [
          "Roles"
        ]
      }
    },
    "/api/v1/organizations/{orgId}/roles/{id}": {
      "get": {
        "description": "Retrieves a specific role by its unique identifier.",
        "operationId": "getRole",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Role UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Role retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoleResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Role not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get role by ID",
        "tags": [
          "Roles"
        ]
      },
      "patch": {
        "description": "Updates an existing custom role. System roles cannot be modified.",
        "operationId": "updateRole",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Role UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateRoleDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Role updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoleResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data or cannot modify system role"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Role not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update custom role",
        "tags": [
          "Roles"
        ]
      },
      "delete": {
        "description": "Deletes a custom role. System roles cannot be deleted.",
        "operationId": "deleteRole",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Role UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Role deleted successfully"
          },
          "400": {
            "description": "Cannot delete system role or role in use"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "Role not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete custom role",
        "tags": [
          "Roles"
        ]
      }
    },
    "/api/v1/audit": {
      "get": {
        "description": "Searches audit logs with filters and pagination.",
        "operationId": "searchAuditLogs",
        "parameters": [
          {
            "name": "userId",
            "required": false,
            "in": "query",
            "description": "Filter by user ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "apiKeyId",
            "required": false,
            "in": "query",
            "description": "Filter by API key ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440001",
              "type": "string"
            }
          },
          {
            "name": "action",
            "required": false,
            "in": "query",
            "description": "Filter by audit action type(s)",
            "schema": {
              "example": [
                "CREATE",
                "UPDATE"
              ],
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "CREATE",
                  "READ",
                  "UPDATE",
                  "DELETE",
                  "LOGIN",
                  "LOGOUT",
                  "LOGIN_FAILED",
                  "PERMISSION_GRANTED",
                  "PERMISSION_DENIED",
                  "EXPORT",
                  "IMPORT",
                  "BULK_UPDATE",
                  "BULK_DELETE",
                  "PASSWORD_CHANGE",
                  "API_KEY_CREATED",
                  "API_KEY_REVOKED",
                  "CERTIFICATE_ISSUED",
                  "CERTIFICATE_REVOKED",
                  "DEPLOYMENT_STARTED",
                  "DEPLOYMENT_COMPLETED",
                  "DEPLOYMENT_FAILED",
                  "DEPLOYMENT_ROLLED_BACK",
                  "DEPLOYMENT_REDEPLOYED",
                  "DEVICE_ONBOARDING_REJECTED",
                  "DEVICE_CAPABILITY_REPORT_REJECTED",
                  "DEVICE_STATUS_REPORT_REJECTED",
                  "DEVICE_DESIRED_STATE_FETCH_REJECTED",
                  "DEVICE_ONBOARDING_REJECTED_SUMMARY"
                ]
              }
            }
          },
          {
            "name": "entityType",
            "required": false,
            "in": "query",
            "description": "Filter by entity type(s)",
            "schema": {
              "example": [
                "Device",
                "User"
              ],
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "entityId",
            "required": false,
            "in": "query",
            "description": "Filter by specific entity ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440002",
              "type": "string"
            }
          },
          {
            "name": "success",
            "required": false,
            "in": "query",
            "description": "Filter by success status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Filter logs from this date",
            "schema": {
              "format": "date-time",
              "example": "2024-01-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "Filter logs until this date",
            "schema": {
              "format": "date-time",
              "example": "2024-12-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "ipAddress",
            "required": false,
            "in": "query",
            "description": "Filter by IP address",
            "schema": {
              "example": "192.168.1.100",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Full-text search in entity name and error message",
            "schema": {
              "example": "device-001",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number",
            "schema": {
              "minimum": 1,
              "default": 1,
              "example": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of results per page",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for cursor-based pagination",
            "schema": {
              "example": "eyJpZCI6IjEyMzQifQ==",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit logs retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditLogListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Search audit logs",
        "tags": [
          "Audit"
        ]
      }
    },
    "/api/v1/audit/stats": {
      "get": {
        "description": "Retrieves audit log statistics for the organization.",
        "operationId": "getAuditStats",
        "parameters": [
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Start date for statistics (ISO 8601 format)",
            "schema": {
              "example": "2024-01-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "End date for statistics (ISO 8601 format)",
            "schema": {
              "example": "2024-12-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditStatsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get audit statistics",
        "tags": [
          "Audit"
        ]
      }
    },
    "/api/v1/audit/export": {
      "get": {
        "description": "Exports audit logs to CSV format. The last two columns are Reason (the free-text reason an actor gave, for example for a tenant switch) and Metadata (the entry metadata as JSON).",
        "operationId": "exportAuditLogs",
        "parameters": [
          {
            "name": "userId",
            "required": false,
            "in": "query",
            "description": "Filter by user ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "apiKeyId",
            "required": false,
            "in": "query",
            "description": "Filter by API key ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440001",
              "type": "string"
            }
          },
          {
            "name": "action",
            "required": false,
            "in": "query",
            "description": "Filter by audit action type(s)",
            "schema": {
              "example": [
                "CREATE",
                "UPDATE"
              ],
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "CREATE",
                  "READ",
                  "UPDATE",
                  "DELETE",
                  "LOGIN",
                  "LOGOUT",
                  "LOGIN_FAILED",
                  "PERMISSION_GRANTED",
                  "PERMISSION_DENIED",
                  "EXPORT",
                  "IMPORT",
                  "BULK_UPDATE",
                  "BULK_DELETE",
                  "PASSWORD_CHANGE",
                  "API_KEY_CREATED",
                  "API_KEY_REVOKED",
                  "CERTIFICATE_ISSUED",
                  "CERTIFICATE_REVOKED",
                  "DEPLOYMENT_STARTED",
                  "DEPLOYMENT_COMPLETED",
                  "DEPLOYMENT_FAILED",
                  "DEPLOYMENT_ROLLED_BACK",
                  "DEPLOYMENT_REDEPLOYED",
                  "DEVICE_ONBOARDING_REJECTED",
                  "DEVICE_CAPABILITY_REPORT_REJECTED",
                  "DEVICE_STATUS_REPORT_REJECTED",
                  "DEVICE_DESIRED_STATE_FETCH_REJECTED",
                  "DEVICE_ONBOARDING_REJECTED_SUMMARY"
                ]
              }
            }
          },
          {
            "name": "entityType",
            "required": false,
            "in": "query",
            "description": "Filter by entity type(s)",
            "schema": {
              "example": [
                "Device",
                "User"
              ],
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "entityId",
            "required": false,
            "in": "query",
            "description": "Filter by specific entity ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440002",
              "type": "string"
            }
          },
          {
            "name": "success",
            "required": false,
            "in": "query",
            "description": "Filter by success status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Filter logs from this date",
            "schema": {
              "format": "date-time",
              "example": "2024-01-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "Filter logs until this date",
            "schema": {
              "format": "date-time",
              "example": "2024-12-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "ipAddress",
            "required": false,
            "in": "query",
            "description": "Filter by IP address",
            "schema": {
              "example": "192.168.1.100",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Full-text search in entity name and error message",
            "schema": {
              "example": "device-001",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number",
            "schema": {
              "minimum": 1,
              "default": 1,
              "example": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of results per page",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for cursor-based pagination",
            "schema": {
              "example": "eyJpZCI6IjEyMzQifQ==",
              "type": "string"
            }
          },
          {
            "name": "maxRecords",
            "required": false,
            "in": "query",
            "description": "Maximum records to export",
            "schema": {
              "minimum": 1,
              "maximum": 100000,
              "default": 10000,
              "example": 10000,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file with audit logs",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Export audit logs",
        "tags": [
          "Audit"
        ]
      }
    },
    "/api/v1/audit/{id}": {
      "get": {
        "description": "Retrieves a specific audit log entry.",
        "operationId": "getAuditLog",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Audit log UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit log retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditLogResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Audit log not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get audit log by ID",
        "tags": [
          "Audit"
        ]
      }
    },
    "/api/v1/audit/entity/{entityType}/{entityId}": {
      "get": {
        "description": "Retrieves audit logs for a specific entity.",
        "operationId": "getEntityAuditLogs",
        "parameters": [
          {
            "name": "entityType",
            "required": true,
            "in": "path",
            "description": "Entity type (e.g., Device, User)",
            "schema": {
              "example": "Device",
              "type": "string"
            }
          },
          {
            "name": "entityId",
            "required": true,
            "in": "path",
            "description": "Entity UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "userId",
            "required": false,
            "in": "query",
            "description": "Filter by user ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "apiKeyId",
            "required": false,
            "in": "query",
            "description": "Filter by API key ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440001",
              "type": "string"
            }
          },
          {
            "name": "action",
            "required": false,
            "in": "query",
            "description": "Filter by audit action type(s)",
            "schema": {
              "example": [
                "CREATE",
                "UPDATE"
              ],
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "CREATE",
                  "READ",
                  "UPDATE",
                  "DELETE",
                  "LOGIN",
                  "LOGOUT",
                  "LOGIN_FAILED",
                  "PERMISSION_GRANTED",
                  "PERMISSION_DENIED",
                  "EXPORT",
                  "IMPORT",
                  "BULK_UPDATE",
                  "BULK_DELETE",
                  "PASSWORD_CHANGE",
                  "API_KEY_CREATED",
                  "API_KEY_REVOKED",
                  "CERTIFICATE_ISSUED",
                  "CERTIFICATE_REVOKED",
                  "DEPLOYMENT_STARTED",
                  "DEPLOYMENT_COMPLETED",
                  "DEPLOYMENT_FAILED",
                  "DEPLOYMENT_ROLLED_BACK",
                  "DEPLOYMENT_REDEPLOYED",
                  "DEVICE_ONBOARDING_REJECTED",
                  "DEVICE_CAPABILITY_REPORT_REJECTED",
                  "DEVICE_STATUS_REPORT_REJECTED",
                  "DEVICE_DESIRED_STATE_FETCH_REJECTED",
                  "DEVICE_ONBOARDING_REJECTED_SUMMARY"
                ]
              }
            }
          },
          {
            "name": "success",
            "required": false,
            "in": "query",
            "description": "Filter by success status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Filter logs from this date",
            "schema": {
              "format": "date-time",
              "example": "2024-01-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "Filter logs until this date",
            "schema": {
              "format": "date-time",
              "example": "2024-12-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "ipAddress",
            "required": false,
            "in": "query",
            "description": "Filter by IP address",
            "schema": {
              "example": "192.168.1.100",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Full-text search in entity name and error message",
            "schema": {
              "example": "device-001",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number",
            "schema": {
              "minimum": 1,
              "default": 1,
              "example": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of results per page",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for cursor-based pagination",
            "schema": {
              "example": "eyJpZCI6IjEyMzQifQ==",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit logs retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditLogListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get audit logs for entity",
        "tags": [
          "Audit"
        ]
      }
    },
    "/api/v1/audit/user/{userId}": {
      "get": {
        "description": "Retrieves audit logs for a specific user.",
        "operationId": "getUserAuditLogs",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "apiKeyId",
            "required": false,
            "in": "query",
            "description": "Filter by API key ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440001",
              "type": "string"
            }
          },
          {
            "name": "action",
            "required": false,
            "in": "query",
            "description": "Filter by audit action type(s)",
            "schema": {
              "example": [
                "CREATE",
                "UPDATE"
              ],
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "CREATE",
                  "READ",
                  "UPDATE",
                  "DELETE",
                  "LOGIN",
                  "LOGOUT",
                  "LOGIN_FAILED",
                  "PERMISSION_GRANTED",
                  "PERMISSION_DENIED",
                  "EXPORT",
                  "IMPORT",
                  "BULK_UPDATE",
                  "BULK_DELETE",
                  "PASSWORD_CHANGE",
                  "API_KEY_CREATED",
                  "API_KEY_REVOKED",
                  "CERTIFICATE_ISSUED",
                  "CERTIFICATE_REVOKED",
                  "DEPLOYMENT_STARTED",
                  "DEPLOYMENT_COMPLETED",
                  "DEPLOYMENT_FAILED",
                  "DEPLOYMENT_ROLLED_BACK",
                  "DEPLOYMENT_REDEPLOYED",
                  "DEVICE_ONBOARDING_REJECTED",
                  "DEVICE_CAPABILITY_REPORT_REJECTED",
                  "DEVICE_STATUS_REPORT_REJECTED",
                  "DEVICE_DESIRED_STATE_FETCH_REJECTED",
                  "DEVICE_ONBOARDING_REJECTED_SUMMARY"
                ]
              }
            }
          },
          {
            "name": "entityType",
            "required": false,
            "in": "query",
            "description": "Filter by entity type(s)",
            "schema": {
              "example": [
                "Device",
                "User"
              ],
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "entityId",
            "required": false,
            "in": "query",
            "description": "Filter by specific entity ID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440002",
              "type": "string"
            }
          },
          {
            "name": "success",
            "required": false,
            "in": "query",
            "description": "Filter by success status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Filter logs from this date",
            "schema": {
              "format": "date-time",
              "example": "2024-01-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "Filter logs until this date",
            "schema": {
              "format": "date-time",
              "example": "2024-12-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "ipAddress",
            "required": false,
            "in": "query",
            "description": "Filter by IP address",
            "schema": {
              "example": "192.168.1.100",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Full-text search in entity name and error message",
            "schema": {
              "example": "device-001",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number",
            "schema": {
              "minimum": 1,
              "default": 1,
              "example": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of results per page",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for cursor-based pagination",
            "schema": {
              "example": "eyJpZCI6IjEyMzQifQ==",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit logs retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditLogListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get audit logs for user",
        "tags": [
          "Audit"
        ]
      }
    },
    "/api/v1/me/auth-event": {
      "post": {
        "description": "Emits a tamper-evident audit-log entry for an auth-flow event observed by the BFF (login, refresh, logout, federated, session termination). The signing uses the same AUDIT_HMAC_SECRET as the rest of the audit pipeline.",
        "operationId": "recordAuthAuditEvent",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthEventDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Audit event recorded"
          },
          "400": {
            "description": "Invalid payload"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Record an authentication audit event (FM-494)",
        "tags": [
          "Audit"
        ]
      }
    },
    "/api/v1/system-roles": {
      "get": {
        "operationId": "SystemRoleController_listRoles",
        "parameters": [
          {
            "name": "userId",
            "required": false,
            "in": "query",
            "description": "Filter by user ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "role",
            "required": false,
            "in": "query",
            "description": "Filter by role type",
            "schema": {
              "type": "string",
              "enum": [
                "SUPERUSER",
                "SUPPORT"
              ]
            }
          },
          {
            "name": "activeOnly",
            "required": false,
            "in": "query",
            "description": "Filter by active status",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items to return",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of items to skip",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SystemRoleListResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List all system roles",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/superusers": {
      "get": {
        "operationId": "SystemRoleController_listSuperusers",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SystemRoleDto"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List all superusers",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/support": {
      "get": {
        "operationId": "SystemRoleController_listSupportUsers",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SystemRoleDto"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List all support users",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/counts": {
      "get": {
        "operationId": "SystemRoleController_getCounts",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Counts of superusers and support users"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get counts of system role users",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/users/{userId}": {
      "get": {
        "operationId": "SystemRoleController_getUserSystemRoles",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSystemRolesDto"
                }
              }
            }
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get system roles for a user",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/users/{userId}/history": {
      "get": {
        "operationId": "SystemRoleController_getRoleHistory",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SystemRoleDto"
                  }
                }
              }
            }
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get role history for a user",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/grant": {
      "post": {
        "operationId": "SystemRoleController_grantRole",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GrantSystemRoleDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SystemRoleDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request"
          },
          "403": {
            "description": "Only superusers can grant roles"
          },
          "404": {
            "description": "User not found"
          },
          "409": {
            "description": "User already has this role"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Grant a system role to a user",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/superuser/{userId}": {
      "post": {
        "operationId": "SystemRoleController_grantSuperuser",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SystemRoleDto"
                }
              }
            }
          },
          "403": {
            "description": "Only superusers can grant roles"
          },
          "404": {
            "description": "User not found"
          },
          "409": {
            "description": "User already has superuser role"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Grant superuser role to a user",
        "tags": [
          "System Roles"
        ]
      },
      "delete": {
        "operationId": "SystemRoleController_revokeSuperuser",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Superuser role revoked"
          },
          "400": {
            "description": "Cannot revoke own superuser role or last superuser"
          },
          "403": {
            "description": "Only superusers can revoke roles"
          },
          "404": {
            "description": "User not found or does not have superuser role"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Revoke superuser role from a user",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/support/{userId}": {
      "post": {
        "operationId": "SystemRoleController_grantSupport",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SystemRoleDto"
                }
              }
            }
          },
          "403": {
            "description": "Only superusers can grant roles"
          },
          "404": {
            "description": "User not found"
          },
          "409": {
            "description": "User already has support role"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Grant support role to a user",
        "tags": [
          "System Roles"
        ]
      },
      "delete": {
        "operationId": "SystemRoleController_revokeSupport",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Support role revoked"
          },
          "403": {
            "description": "Only superusers can revoke roles"
          },
          "404": {
            "description": "User not found or does not have support role"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Revoke support role from a user",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/revoke": {
      "delete": {
        "operationId": "SystemRoleController_revokeRole",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RevokeSystemRoleDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Role revoked"
          },
          "400": {
            "description": "Cannot revoke own superuser role or last superuser"
          },
          "403": {
            "description": "Only superusers can revoke roles"
          },
          "404": {
            "description": "User or role not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Revoke a system role from a user",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/system-roles/me": {
      "get": {
        "operationId": "SystemRoleController_getMySystemRoles",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSystemRolesDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get my system roles",
        "tags": [
          "System Roles"
        ]
      }
    },
    "/api/v1/tenant/context": {
      "get": {
        "operationId": "TenantSwitchController_getTenantContext",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantContextDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The caller lacks the permission, is an API key (API keys cannot use tenant switches), or is not a member of the organization named by x-organization-id. This endpoint is a global operation, so it never returns TENANT_SWITCH_REQUIRED."
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get current tenant context",
        "tags": [
          "Tenant Switch"
        ]
      }
    },
    "/api/v1/tenant/switch": {
      "post": {
        "description": "Switching to a tenant the caller is a member of (a home tenant) creates no switch: any open switch is ended and the response is 204. A superadmin switching into any other tenant must give a reason (3-500 characters); one switch is opened and returned with 201. Not available to API keys.",
        "operationId": "TenantSwitchController_switchTenant",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SwitchTenantDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Switch opened",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantSwitchDto"
                }
              }
            }
          },
          "204": {
            "description": "Home tenant: any open switch ended, none created"
          },
          "400": {
            "description": "Validation failed, reason missing or too short, or fromOrganizationId not allowed"
          },
          "403": {
            "description": "Forbidden. Standard error body (message in parentheses): the caller is not a member of the organization and has no system role (Not a member of this organization); the organization does not exist (Organization not found) or is inactive (Organization is inactive); the caller is an API key (API key not authorized for this organization, or API keys cannot use tenant switches). Switching is a global operation, so a missing open switch does not apply to this endpoint."
          },
          "409": {
            "description": "A concurrent switch by the same user won the race"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Switch to a different tenant",
        "tags": [
          "Tenant Switch"
        ]
      },
      "patch": {
        "description": "Moves the expiry of the caller's own open switch to a later time, at most 72 hours from now, with a reason (3-500 characters). Only a superadmin or support user holds switches. One audit entry (action extend) records the reason and the previous and new expiry. Not available to API keys.",
        "operationId": "TenantSwitchController_extendSwitch",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExtendTenantSwitchDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Switch extended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantSwitchDto"
                }
              }
            }
          },
          "400": {
            "description": "Reason missing or out of range, expiresAt invalid, not later than the current expiry, or beyond the 72 h cap"
          },
          "403": {
            "description": "No system role, or the caller is an API key (API keys cannot use tenant switches)"
          },
          "404": {
            "description": "No open tenant switch"
          },
          "409": {
            "description": "The switch has expired, or was ended or extended concurrently"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Extend the open tenant switch",
        "tags": [
          "Tenant Switch"
        ]
      },
      "delete": {
        "description": "Ends the open switch. Not available to API keys.",
        "operationId": "TenantSwitchController_returnToDefault",
        "parameters": [
          {
            "name": "trigger",
            "required": false,
            "in": "query",
            "description": "Why the switch ends; recorded in the audit trail (default: user)",
            "schema": {
              "enum": [
                "user",
                "logout"
              ],
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Returned to default tenant"
          },
          "400": {
            "description": "No active tenant switch, or invalid trigger"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission, or the caller is an API key"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Return to default tenant",
        "tags": [
          "Tenant Switch"
        ]
      }
    },
    "/api/v1/tenant/my-tenants": {
      "get": {
        "description": "Returns the user's organization memberships. Superusers/support see every organization.",
        "operationId": "TenantSwitchController_getMyTenants",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MyTenantListDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The caller lacks the permission, or is an API key (API keys cannot use tenant switches)."
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List tenants the current user can access",
        "tags": [
          "Tenant Switch"
        ]
      }
    },
    "/api/v1/tenant/switch/history": {
      "get": {
        "operationId": "TenantSwitchController_getSwitchHistory",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of switches to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of switches to skip",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantSwitchListResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid limit or offset"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get my tenant switch history",
        "tags": [
          "Tenant Switch"
        ]
      }
    },
    "/api/v1/tenant/switches/active": {
      "get": {
        "operationId": "TenantSwitchController_getActiveSwitches",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of switches to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of switches to skip",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantSwitchListResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid limit or offset"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get all active tenant switches (admin)",
        "tags": [
          "Tenant Switch"
        ]
      }
    },
    "/api/v1/activity/recent": {
      "get": {
        "description": "Retrieves recent activity events (deployment status changes, device registrations, certificate issuances) for the current organization. Used by the dashboard.",
        "operationId": "getRecentActivity",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of items to return",
            "schema": {
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent activity retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecentActivityResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get recent activity",
        "tags": [
          "Activity"
        ]
      }
    },
    "/api/v1/activity": {
      "get": {
        "description": "Retrieves activity logs across all users and tenants. Requires Super Admin privileges.",
        "operationId": "listActivity",
        "parameters": [
          {
            "name": "userId",
            "required": false,
            "in": "query",
            "description": "Filter by user ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "required": false,
            "in": "query",
            "description": "Filter by tenant/organization ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "activityTypes",
            "required": false,
            "in": "query",
            "description": "Filter by activity types",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "targetUserId",
            "required": false,
            "in": "query",
            "description": "Filter by target user ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Filter by start date",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "Filter by end date",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "success",
            "required": false,
            "in": "query",
            "description": "Filter by success status",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search text",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Offset for pagination",
            "schema": {
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activity list retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminActivityListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List all activity",
        "tags": [
          "Activity"
        ]
      }
    },
    "/api/v1/activity/users/{userId}": {
      "get": {
        "description": "Retrieves activity logs for a specific user. Requires Super Admin privileges.",
        "operationId": "getActivityByUser",
        "parameters": [
          {
            "name": "userId",
            "required": true,
            "in": "path",
            "description": "User UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "required": false,
            "in": "query",
            "description": "Filter by tenant/organization ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "activityTypes",
            "required": false,
            "in": "query",
            "description": "Filter by activity types",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "targetUserId",
            "required": false,
            "in": "query",
            "description": "Filter by target user ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Filter by start date",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "Filter by end date",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "success",
            "required": false,
            "in": "query",
            "description": "Filter by success status",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search text",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Offset for pagination",
            "schema": {
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "User activity retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminActivityListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "User not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get activity by user",
        "tags": [
          "Activity"
        ]
      }
    },
    "/api/v1/activity/organizations/{orgId}": {
      "get": {
        "description": "Retrieves activity logs for a specific organization/tenant. Requires Super Admin privileges.",
        "operationId": "getActivityByOrganization",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "userId",
            "required": false,
            "in": "query",
            "description": "Filter by user ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "required": false,
            "in": "query",
            "description": "Filter by tenant/organization ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "activityTypes",
            "required": false,
            "in": "query",
            "description": "Filter by activity types",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "targetUserId",
            "required": false,
            "in": "query",
            "description": "Filter by target user ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "startDate",
            "required": false,
            "in": "query",
            "description": "Filter by start date",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": false,
            "in": "query",
            "description": "Filter by end date",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "success",
            "required": false,
            "in": "query",
            "description": "Filter by success status",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search text",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Offset for pagination",
            "schema": {
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organization activity retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminActivityListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get activity by organization",
        "tags": [
          "Activity"
        ]
      }
    },
    "/api/v1/activity/organizations/{orgId}/summary": {
      "get": {
        "description": "Retrieves activity summary for an organization including active users, login counts, etc. Requires Super Admin privileges.",
        "operationId": "getOrganizationActivitySummary",
        "parameters": [
          {
            "name": "orgId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "sinceDays",
            "required": false,
            "in": "query",
            "description": "Number of days to look back",
            "schema": {
              "example": 30,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Organization activity summary retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantActivitySummaryDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          },
          "404": {
            "description": "Organization not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get organization activity summary",
        "tags": [
          "Activity"
        ]
      }
    },
    "/api/v1/activity/reports/inactivity": {
      "get": {
        "description": "Generates a report of inactive tenants. Requires Super Admin privileges.",
        "operationId": "getInactivityReport",
        "parameters": [
          {
            "name": "thresholdDays",
            "required": false,
            "in": "query",
            "description": "Number of days to consider a tenant inactive",
            "schema": {
              "example": 30,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Inactivity report generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InactivityReportDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get inactivity report",
        "tags": [
          "Activity"
        ]
      }
    },
    "/api/v1/activity/export": {
      "post": {
        "description": "Exports activity logs to CSV or JSON format. Requires Super Admin privileges.",
        "operationId": "exportActivity",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExportActivityDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Activity exported successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - Super Admin required"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Export activity logs",
        "tags": [
          "Activity"
        ]
      }
    },
    "/api/v1/ca-certificates": {
      "get": {
        "description": "Retrieves a paginated list of CA certificates for the organization.",
        "operationId": "listCaCertificates",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of CA certificates to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of CA certificates to skip",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for pagination (CA certificate ID)",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "isRoot",
            "required": false,
            "in": "query",
            "description": "Filter by root CA status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "isDefault",
            "required": false,
            "in": "query",
            "description": "Filter by default signing CA status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "includeRetired",
            "required": false,
            "in": "query",
            "description": "Include retired CA certificates",
            "schema": {
              "default": false,
              "example": false,
              "type": "boolean"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of CA certificates retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaCertificateListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List CA certificates",
        "tags": [
          "CA Certificates"
        ]
      },
      "post": {
        "description": "Upload a new CA certificate and private key. The private key will be encrypted at rest.",
        "operationId": "createCaCertificate",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCaCertificateDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "CA certificate uploaded successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaCertificateResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Invalid certificate or private key format"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions (admin only)"
          },
          "409": {
            "description": "Conflict - CA certificate with same fingerprint already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Upload CA certificate",
        "tags": [
          "CA Certificates"
        ]
      }
    },
    "/api/v1/ca-certificates/{id}": {
      "get": {
        "description": "Retrieves detailed information about a specific CA certificate.",
        "operationId": "getCaCertificate",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "CA Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CA certificate retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaCertificateResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "CA certificate not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get CA certificate by ID",
        "tags": [
          "CA Certificates"
        ]
      },
      "delete": {
        "description": "Permanently delete a CA certificate. Consider retiring instead for audit trail.",
        "operationId": "deleteCaCertificate",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "CA Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "CA certificate deleted successfully"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions (admin only)"
          },
          "404": {
            "description": "CA certificate not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete CA certificate",
        "tags": [
          "CA Certificates"
        ]
      }
    },
    "/api/v1/ca-certificates/generate": {
      "post": {
        "description": "Generate a new self-signed CA certificate with RSA 4096 key pair. For development and testing only. In production, use a proper PKI.",
        "operationId": "generateCaCertificate",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateCaCertificateDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "CA certificate generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaCertificateResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Invalid parameters"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions (admin only)"
          },
          "409": {
            "description": "Conflict - CA certificate with same fingerprint already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Generate self-signed CA certificate",
        "tags": [
          "CA Certificates"
        ]
      }
    },
    "/api/v1/ca-certificates/{id}/default": {
      "patch": {
        "description": "Set this CA certificate as the default signing CA for new device certificates.",
        "operationId": "setCaCertificateDefault",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "CA Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CA certificate set as default successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaCertificateResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Cannot set retired CA as default"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions (admin only)"
          },
          "404": {
            "description": "CA certificate not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Set as default CA",
        "tags": [
          "CA Certificates"
        ]
      }
    },
    "/api/v1/ca-certificates/{id}/renew": {
      "post": {
        "description": "Re-issues the CA certificate with the same subject and key pair, a new serial number and a new validity period (now + validityDays), signed the way the original was (by itself when self-signed, otherwise by its parent CA). The stored certificate, fingerprint and validity dates are replaced by those of the new certificate.",
        "operationId": "renewCaCertificate",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "CA Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenewCaCertificateDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "CA certificate renewed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaCertificateResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Cannot renew a retired CA certificate"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions (admin only)"
          },
          "404": {
            "description": "CA certificate not found"
          },
          "409": {
            "description": "Conflict - The CA was issued by a CA whose private key logiccloud orchestrate does not hold"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Renew CA certificate",
        "tags": [
          "CA Certificates"
        ]
      }
    },
    "/api/v1/ca-certificates/{id}/download": {
      "get": {
        "description": "Download the public CA certificate in PEM format. Private key is never exposed.",
        "operationId": "downloadCaCertificate",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "CA Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CA certificate PEM file",
            "content": {
              "application/x-pem-file": {
                "schema": {
                  "type": "string",
                  "example": "-----BEGIN CERTIFICATE-----\nMIIBkTCC...\n-----END CERTIFICATE-----"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          },
          "404": {
            "description": "CA certificate not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Download CA certificate",
        "tags": [
          "CA Certificates"
        ]
      }
    },
    "/api/v1/ca-certificates/{id}/retire": {
      "post": {
        "description": "Retire a CA certificate. Retired certificates are kept for audit but cannot be used for signing.",
        "operationId": "retireCaCertificate",
        "parameters": [
          {
            "name": "x-user-id",
            "required": true,
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "CA Certificate UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CA certificate retired successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaCertificateResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - CA certificate is already retired"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions (admin only)"
          },
          "404": {
            "description": "CA certificate not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Retire CA certificate",
        "tags": [
          "CA Certificates"
        ]
      }
    },
    "/api/v1/certificates": {
      "get": {
        "description": "Retrieves all certificates (device and CA) for the organization with filtering and pagination support.",
        "operationId": "listCertificates",
        "parameters": [
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by certificate status",
            "schema": {
              "default": "all",
              "type": "string",
              "enum": [
                "all",
                "valid",
                "expiring",
                "expired",
                "revoked",
                "unknown"
              ]
            }
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "Filter by certificate type",
            "schema": {
              "default": "all",
              "type": "string",
              "enum": [
                "all",
                "device",
                "ca"
              ]
            }
          },
          {
            "name": "deviceId",
            "required": false,
            "in": "query",
            "description": "Filter by device ID (only for device certificates)",
            "schema": {
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search by subject or fingerprint",
            "schema": {
              "example": "CN=device-001",
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of certificates retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List all certificates",
        "tags": [
          "Certificates"
        ]
      }
    },
    "/api/v1/certificates/expiring": {
      "get": {
        "description": "Retrieves certificates that are expiring within a specified number of days.",
        "operationId": "getExpiringCertificates",
        "parameters": [
          {
            "name": "days",
            "required": false,
            "in": "query",
            "description": "Number of days to look ahead for expiring certificates",
            "schema": {
              "minimum": 1,
              "maximum": 365,
              "default": 30,
              "example": 30,
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based)",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of items per page",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Expiring certificates retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get expiring certificates",
        "tags": [
          "Certificates"
        ]
      }
    },
    "/api/v1/certificates/stats": {
      "get": {
        "description": "Retrieves certificate statistics including total, valid, expiring, expired, and revoked counts.",
        "operationId": "getCertificateStats",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Certificate statistics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateStatsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden - Insufficient permissions"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get certificate statistics",
        "tags": [
          "Certificates"
        ]
      }
    },
    "/api/v1/registries/git": {
      "post": {
        "description": "Creates a new Git registry for the current organization.",
        "operationId": "createGitRegistry",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateGitRegistryDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Git registry created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRegistryResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Registry with this name already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create Git registry",
        "tags": [
          "Registries"
        ]
      },
      "get": {
        "description": "Retrieves a list of Git registries for the current organization.",
        "operationId": "listGitRegistries",
        "parameters": [
          {
            "name": "name",
            "required": false,
            "in": "query",
            "description": "Filter by registry name (partial match)",
            "schema": {
              "example": "workloads",
              "type": "string"
            }
          },
          {
            "name": "enabled",
            "required": false,
            "in": "query",
            "description": "Filter by enabled status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "authType",
            "required": false,
            "in": "query",
            "description": "Filter by authentication type",
            "schema": {
              "example": "TOKEN",
              "type": "string",
              "enum": [
                "NONE",
                "HTTPS_TOKEN",
                "SSH_KEY"
              ]
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search across name and URL",
            "schema": {
              "example": "github",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of items to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of items to skip",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for pagination (registry ID)",
            "schema": {
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Git registries retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRegistryListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List Git registries",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/git/{id}": {
      "get": {
        "description": "Retrieves a Git registry by its unique identifier.",
        "operationId": "getGitRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Git registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Git registry retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRegistryResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Git registry not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get Git registry by ID",
        "tags": [
          "Registries"
        ]
      },
      "patch": {
        "description": "Updates an existing Git registry.",
        "operationId": "updateGitRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Git registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateGitRegistryDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Git registry updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRegistryResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Git registry not found"
          },
          "409": {
            "description": "Registry with this name already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update Git registry",
        "tags": [
          "Registries"
        ]
      },
      "delete": {
        "description": "Deletes a Git registry.",
        "operationId": "deleteGitRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Git registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Git registry deleted successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Git registry not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete Git registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/git/{id}/enable": {
      "post": {
        "description": "Enables a Git registry.",
        "operationId": "enableGitRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Git registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Git registry enabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRegistryResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Git registry not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Enable Git registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/git/{id}/disable": {
      "post": {
        "description": "Disables a Git registry.",
        "operationId": "disableGitRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Git registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Git registry disabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRegistryResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Git registry not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Disable Git registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/git/{id}/sync": {
      "post": {
        "description": "Triggers a manual sync operation for a Git registry. This clones/pulls the repository and processes application manifests.",
        "operationId": "syncGitRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Git registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sync operation completed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRegistrySyncResultDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden\n\nForbidden — caller lacks required permission"
          },
          "404": {
            "description": "Git registry not found"
          },
          "409": {
            "description": "Git registry is disabled"
          },
          "503": {
            "description": "Application service unavailable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Trigger sync for Git registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/oci": {
      "post": {
        "description": "Creates a new OCI registry for the current organization.",
        "operationId": "createOciRegistry",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOciRegistryDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OCI registry created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistryResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "409": {
            "description": "Registry with this name already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create OCI registry",
        "tags": [
          "Registries"
        ]
      },
      "get": {
        "description": "Retrieves a list of OCI registries for the current organization.",
        "operationId": "listOciRegistries",
        "parameters": [
          {
            "name": "name",
            "required": false,
            "in": "query",
            "description": "Filter by registry name (partial match)",
            "schema": {
              "example": "container",
              "type": "string"
            }
          },
          {
            "name": "enabled",
            "required": false,
            "in": "query",
            "description": "Filter by enabled status",
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "name": "registryType",
            "required": false,
            "in": "query",
            "description": "Filter by registry type",
            "schema": {
              "example": "GENERIC",
              "type": "string",
              "enum": [
                "GENERIC"
              ]
            }
          },
          {
            "name": "authType",
            "required": false,
            "in": "query",
            "description": "Filter by authentication type",
            "schema": {
              "example": "BASIC",
              "type": "string",
              "enum": [
                "NONE",
                "BASIC",
                "TOKEN"
              ]
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search across name, URL, and description",
            "schema": {
              "example": "docker",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of items to return",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of items to skip",
            "schema": {
              "minimum": 0,
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for pagination (registry ID)",
            "schema": {
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OCI registries retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistryListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List OCI registries",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/oci/{id}": {
      "get": {
        "description": "Retrieves an OCI registry by its unique identifier.",
        "operationId": "getOciRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "OCI registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OCI registry retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistryResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "OCI registry not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get OCI registry by ID",
        "tags": [
          "Registries"
        ]
      },
      "patch": {
        "description": "Updates an existing OCI registry.",
        "operationId": "updateOciRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "OCI registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateOciRegistryDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OCI registry updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistryResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "OCI registry not found"
          },
          "409": {
            "description": "Registry with this name already exists"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update OCI registry",
        "tags": [
          "Registries"
        ]
      },
      "delete": {
        "description": "Deletes an OCI registry.",
        "operationId": "deleteOciRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "OCI registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OCI registry deleted successfully"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "OCI registry not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete OCI registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/oci/{id}/enable": {
      "post": {
        "description": "Enables an OCI registry.",
        "operationId": "enableOciRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "OCI registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OCI registry enabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistryResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "OCI registry not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Enable OCI registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/oci/{id}/disable": {
      "post": {
        "description": "Disables an OCI registry.",
        "operationId": "disableOciRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "OCI registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OCI registry disabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistryResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "OCI registry not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Disable OCI registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/oci/{id}/health-check": {
      "post": {
        "description": "Triggers a manual health check for an OCI registry. Checks connectivity using the OCI Distribution Spec /v2/ endpoint.",
        "operationId": "healthCheckOciRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "OCI registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Health check completed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistryHealthCheckResultDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden\n\nForbidden — caller lacks required permission"
          },
          "404": {
            "description": "OCI registry not found"
          },
          "503": {
            "description": "Application service unavailable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Trigger health check for OCI registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/oci/{id}/sync": {
      "post": {
        "description": "Triggers a manual sync operation for an OCI registry. This discovers applications in the registry and imports them to the catalog.",
        "operationId": "syncOciRegistry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "OCI registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sync operation completed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistrySyncResultDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden\n\nForbidden — caller lacks required permission"
          },
          "404": {
            "description": "OCI registry not found"
          },
          "409": {
            "description": "OCI registry is disabled"
          },
          "503": {
            "description": "Application service unavailable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Trigger sync for OCI registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/registries/oci/{id}/sync-history": {
      "get": {
        "description": "Returns the 30-day sync history for a tenant OCI registry (newest first). Each entry captures status, counters, errors, duration, and trigger source.",
        "operationId": "listOciRegistrySyncHistory",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "OCI registry UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sync history returned successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OciRegistrySyncHistoryListDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "OCI registry not found"
          },
          "503": {
            "description": "Application service unavailable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List sync history for OCI registry",
        "tags": [
          "Registries"
        ]
      }
    },
    "/api/v1/system/flecs-stores/{organizationId}": {
      "get": {
        "description": "Retrieves the FLECS marketplace store configuration for an organization. Requires Super Admin privileges. There is no credential concept for FLECS — the catalog endpoint is public and unauthenticated, so this returns only the store ID, whether it is enabled, and discovery sync diagnostics.",
        "operationId": "getFlecsStore",
        "parameters": [
          {
            "name": "organizationId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Configuration retrieved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlecsStoreResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - requires Super Admin privileges"
          },
          "404": {
            "description": "No FLECS store configured for this organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get a tenant FLECS store configuration",
        "tags": [
          "FLECS Stores"
        ]
      },
      "put": {
        "description": "Upserts the FLECS marketplace store configuration for an organization. The FLECS catalog endpoint is public and unauthenticated, so a store ID plus whether it is enabled is the entire configuration — no credentials are ever required or stored. Requires Super Admin privileges.",
        "operationId": "upsertFlecsStore",
        "parameters": [
          {
            "name": "organizationId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpsertFlecsStoreDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Configuration saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlecsStoreResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input — for example a missing or non-positive storeId"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - requires Super Admin privileges"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create or replace a tenant FLECS store configuration",
        "tags": [
          "FLECS Stores"
        ]
      },
      "delete": {
        "description": "Soft-deletes the configuration and stops syncing. Requires Super Admin privileges.",
        "operationId": "deleteFlecsStore",
        "parameters": [
          {
            "name": "organizationId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Configuration removed"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - requires Super Admin privileges"
          },
          "404": {
            "description": "No FLECS store configured for this organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Remove a tenant FLECS store configuration",
        "tags": [
          "FLECS Stores"
        ]
      }
    },
    "/api/v1/system/flecs-stores/{organizationId}/enable": {
      "post": {
        "description": "Activates catalog syncing for the configured store. Requires Super Admin privileges.",
        "operationId": "enableFlecsStore",
        "parameters": [
          {
            "name": "organizationId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Store enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlecsStoreResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - requires Super Admin privileges"
          },
          "404": {
            "description": "No FLECS store configured for this organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Enable a tenant FLECS store",
        "tags": [
          "FLECS Stores"
        ]
      }
    },
    "/api/v1/system/flecs-stores/{organizationId}/disable": {
      "post": {
        "description": "Stops catalog syncing while retaining the configuration. Requires Super Admin privileges.",
        "operationId": "disableFlecsStore",
        "parameters": [
          {
            "name": "organizationId",
            "required": true,
            "in": "path",
            "description": "Organization UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Store disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlecsStoreResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - requires Super Admin privileges"
          },
          "404": {
            "description": "No FLECS store configured for this organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Disable a tenant FLECS store",
        "tags": [
          "FLECS Stores"
        ]
      }
    },
    "/api/v1/flecs-store/sync": {
      "post": {
        "description": "Triggers an immediate sync of the current organization’s FLECS marketplace store, ahead of the automatic 5-minute schedule. Returns null when no store is configured, or when the FLECS catalog discovery call itself failed before the underlying OCI sync could start (see the store’s lastSyncError for the reason).",
        "operationId": "triggerFlecsStoreSync",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sync operation completed (or discovery failed — see lastSyncError)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlecsStoreSyncResultDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "503": {
            "description": "Application service unavailable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Trigger a manual sync for the tenant FLECS store",
        "tags": [
          "FLECS Stores"
        ]
      }
    },
    "/api/v1/flecs-store/sync/progress": {
      "get": {
        "description": "Returns the current sync run’s per-repository status (checking/added/updated/rejected), if a run is currently in flight. registryId is null when nothing is running — the expected state between the automatic 5-minute ticks, not an error.",
        "operationId": "getFlecsStoreSyncProgress",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Progress returned (idle shape when nothing is running)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlecsStoreSyncProgressDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "503": {
            "description": "Application service unavailable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Live per-repository sync progress for the tenant FLECS store",
        "tags": [
          "FLECS Stores"
        ]
      }
    },
    "/api/v1/flecs-store/sync-history": {
      "get": {
        "description": "Returns the sync history for the current organization’s FLECS marketplace store (newest first). Each entry includes the per-product breakdown of catalog products imported vs. skipped in that run, and why a product was skipped.",
        "operationId": "listFlecsSyncHistory",
        "parameters": [
          {
            "name": "limit",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sync history returned successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlecsStoreSyncHistoryListDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "503": {
            "description": "Application service unavailable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List sync history for the tenant FLECS store",
        "tags": [
          "FLECS Stores"
        ]
      }
    },
    "/api/v1/applications": {
      "get": {
        "description": "Retrieves a paginated list of applications with optional filtering by category, vendor, and deployment profile.",
        "operationId": "listApplications",
        "parameters": [
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Search term to filter applications by name, description, or vendor",
            "schema": {
              "example": "mosquitto",
              "type": "string"
            }
          },
          {
            "name": "category",
            "required": false,
            "in": "query",
            "description": "Filter by category",
            "schema": {
              "example": "IoT",
              "type": "string"
            }
          },
          {
            "name": "vendor",
            "required": false,
            "in": "query",
            "description": "Filter by vendor",
            "schema": {
              "example": "Eclipse Foundation",
              "type": "string"
            }
          },
          {
            "name": "deploymentProfile",
            "required": false,
            "in": "query",
            "description": "Filter by deployment profile type",
            "schema": {
              "type": "string",
              "enum": [
                "DOCKER_COMPOSE",
                "HELM",
                "K8S_MANIFEST",
                "PODMAN",
                "QUADLET"
              ]
            }
          },
          {
            "name": "availability",
            "required": false,
            "in": "query",
            "description": "Filter by availability status",
            "schema": {
              "type": "string",
              "enum": [
                "AVAILABLE",
                "SOURCE_UNAVAILABLE"
              ]
            }
          },
          {
            "name": "lifecycleStatus",
            "required": false,
            "in": "query",
            "description": "Filter by version lifecycle status (FM-1004). Matches applications with at least one version in this status.",
            "schema": {
              "type": "string",
              "enum": [
                "ACTIVE",
                "DEPRECATED",
                "RETIRED"
              ]
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of results to return",
            "schema": {
              "minimum": 1,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of results to skip",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor for pagination",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sortBy",
            "required": false,
            "in": "query",
            "description": "Field to sort by",
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "updatedAt",
                "createdAt"
              ]
            }
          },
          {
            "name": "sortOrder",
            "required": false,
            "in": "query",
            "description": "Sort order",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of applications retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplicationListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "503": {
            "description": "Service unavailable - Application service is not reachable"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List applications",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/categories": {
      "get": {
        "description": "Retrieves a list of unique application categories",
        "operationId": "getCategories",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Categories retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoriesResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get unique categories",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/vendors": {
      "get": {
        "description": "Retrieves a list of unique application vendors",
        "operationId": "getVendors",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Vendors retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VendorsResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get unique vendors",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/{id}": {
      "get": {
        "description": "Retrieves detailed information about a specific application including all versions.",
        "operationId": "getApplication",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Application retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplicationDetailsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Application not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get application by ID",
        "tags": [
          "Applications"
        ]
      },
      "delete": {
        "description": "Soft-deletes the application and all of its versions. Refused with 409 if any version has active deployments, or if the application is still provided by a live registry source. Restricted to superadmins.",
        "operationId": "deleteApplication",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Application deleted"
          },
          "403": {
            "description": "Superadmin access required"
          },
          "404": {
            "description": "Application not found"
          },
          "409": {
            "description": "Active deployments still reference the application, or a live registry source still provides it"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete an application package",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/{id}/versions": {
      "get": {
        "description": "Retrieves all versions for an application package.",
        "operationId": "getApplicationVersions",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "example": "550e8400-e29b-41d4-a716-446655440000",
              "type": "string"
            }
          },
          {
            "name": "lifecycleStatus",
            "required": false,
            "in": "query",
            "description": "Return only versions in this lifecycle status",
            "schema": {
              "type": "string",
              "enum": [
                "ACTIVE",
                "DEPRECATED",
                "RETIRED"
              ]
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Application versions retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplicationVersionsResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Application not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get application versions",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/{id}/versions/{version}/lifecycle-status": {
      "patch": {
        "description": "Sets the operator-owned lifecycle status (ACTIVE, DEPRECATED, RETIRED) of one version (FM-1004). A RETIRED version cannot be selected for new deployments; existing deployments are unaffected. DEPRECATED stays deployable. Registry syncs never change this status.",
        "operationId": "setApplicationVersionLifecycleStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "version",
            "required": true,
            "in": "path",
            "description": "Version string",
            "schema": {
              "example": "2.0.18",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateApplicationLifecycleStatusDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lifecycle status updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplicationDescriptionDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid lifecycle status"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Version not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Set the lifecycle status of an application version",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/{id}/versions/{version}": {
      "delete": {
        "description": "Soft-deletes one version that no registry provides (FM-1467). Refused with 409 while any deployment other than REMOVED references the version, or while a registry source still provides it. When the last version goes, the application goes too. A later sync that provides the version again restores it. Needs the applications:delete permission in the tenant; superadmin is not required.",
        "operationId": "deleteApplicationVersion",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "version",
            "required": true,
            "in": "path",
            "description": "Version string",
            "schema": {
              "example": "2.0.18",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Version deleted"
          },
          "400": {
            "description": "Invalid application id or version"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Version not found"
          },
          "409": {
            "description": "Active deployments still reference the version, or a registry still provides it"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete an application version",
        "tags": [
          "Applications"
        ]
      },
      "get": {
        "description": "Retrieves a specific version of an application.",
        "operationId": "getApplicationVersion",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "version",
            "required": true,
            "in": "path",
            "description": "Version string (semver)",
            "schema": {
              "example": "2.0.18",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Application version retrieved successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Version not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get specific application version",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/{id}/deployments": {
      "get": {
        "description": "Retrieves all deployments across all versions of an application package.",
        "operationId": "listApplicationDeployments",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Max items to return (default 20)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of items to skip (default 0)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployments retrieved successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Application not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List deployments for an application",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/{id}/versions/{version}/deployments": {
      "get": {
        "description": "Retrieves deployments for a specific version of an application.",
        "operationId": "listVersionDeployments",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "version",
            "required": true,
            "in": "path",
            "description": "Version string",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Max items to return (default 20)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of items to skip (default 0)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deployments retrieved successfully"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List deployments for a specific version",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/applications/{id}/icon": {
      "get": {
        "description": "Retrieves the icon image for an application.",
        "operationId": "getApplicationIcon",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Application package UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Icon retrieved successfully",
            "content": {
              "image/png": {},
              "image/svg+xml": {},
              "image/jpeg": {}
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Icon not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get application icon",
        "tags": [
          "Applications"
        ]
      }
    },
    "/api/v1/devices/{id}/telemetry": {
      "get": {
        "description": "Retrieves metric snapshots for a device within a time range, with optional downsampling.",
        "operationId": "getDeviceTelemetry",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": false,
            "in": "query",
            "description": "Start of time range (ISO 8601)",
            "schema": {
              "example": "2026-03-17T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "description": "End of time range (ISO 8601)",
            "schema": {
              "example": "2026-03-18T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "interval",
            "required": false,
            "in": "query",
            "description": "Aggregation interval for downsampling",
            "schema": {
              "default": "1m",
              "type": "string",
              "enum": [
                "1m",
                "5m",
                "1h",
                "1d"
              ]
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Telemetry data retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TelemetryListResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get device telemetry",
        "tags": [
          "Device Telemetry"
        ]
      }
    },
    "/api/v1/devices/{id}/telemetry/latest": {
      "get": {
        "description": "Retrieves the most recent metric snapshot with computed percentages.",
        "operationId": "getDeviceTelemetryLatest",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Latest telemetry retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MetricSnapshotDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Device not found or no telemetry data"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get latest device telemetry",
        "tags": [
          "Device Telemetry"
        ]
      }
    },
    "/api/v1/devices/{id}/metrics/raw": {
      "get": {
        "description": "Retrieves every fm_device_* metric series currently reporting for this device, unmapped onto the fixed CPU/memory/disk field set the curated telemetry endpoints project onto (FM-1148). Useful for confirming telemetry is actually arriving when a device sends a metric shape the curated cards don't fully cover.",
        "operationId": "getDeviceRawMetrics",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Raw metrics retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RawMetricsResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get every raw device metric currently reported",
        "tags": [
          "Device Telemetry"
        ]
      }
    },
    "/api/v1/devices/{id}/logs": {
      "get": {
        "description": "Retrieves log entries for a device with cursor-based pagination, newest first.",
        "operationId": "getDeviceLogs",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "severity",
            "required": false,
            "in": "query",
            "description": "Filter by severity level",
            "schema": {
              "type": "string",
              "enum": [
                "DEBUG",
                "INFO",
                "WARN",
                "ERROR"
              ]
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of log entries to return. Raised to 1000 (FM-772) to guarantee at least the last 1000 lines are retrievable for an active device.",
            "schema": {
              "minimum": 1,
              "maximum": 1000,
              "default": 100,
              "type": "number"
            }
          },
          {
            "name": "before",
            "required": false,
            "in": "query",
            "description": "Cursor for pagination (timestamp of last entry)",
            "schema": {
              "example": "2026-03-17T12:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "source",
            "required": false,
            "in": "query",
            "description": "Filter by log source (container name, service, or \"system\")",
            "schema": {
              "example": "my-container",
              "type": "string"
            }
          },
          {
            "name": "q",
            "required": false,
            "in": "query",
            "description": "Full-text search against the log message body. Matched case-insensitively as a substring, server-side, against Loki (index-backed by the label selector; the search itself is a line filter evaluated before JSON parsing). Composes with `severity` and `source`.",
            "schema": {
              "example": "connection refused",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Log entries retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogListResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get device logs",
        "tags": [
          "Device Telemetry"
        ]
      }
    },
    "/api/v1/devices/{id}/logs/sources": {
      "get": {
        "description": "Retrieves the distinct workload/container/service `source` values seen for this device within the log retention window, so the UI can populate a workload picker without scanning the full log.",
        "operationId": "getDeviceLogSources",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Log sources retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogSourcesResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Device not found"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get distinct device log sources",
        "tags": [
          "Device Telemetry"
        ]
      }
    },
    "/api/v1/devices/{id}/logs/stream": {
      "get": {
        "description": "Server-Sent Events stream of new log lines for a device as they arrive, honoring the same `severity`/`source`/`q` filters as the paged `/logs` endpoint (FM-773, composed through the same `buildDeviceLogQuery()` builder). Implemented as short-interval polling of Loki's `query_range`, re-emitted as SSE — see `LogStreamService` for why. Named events on the stream: `log` (a `LogEntryDto`, `id` set to a resumable `<timestampMs>:<seq>` cursor — reconnect with the `Last-Event-ID` header to resume exactly after it, which browsers do automatically on a dropped connection), `heartbeat` (keepalive, ~15s, survives idle proxies), `skip` (`{ skipped: number }`, emitted when the server had to drop the oldest lines of a poll tick under backpressure rather than buffer unboundedly), `error` (a transient upstream query failure; the stream keeps polling), and `permission-revoked` (the stream ends immediately after — the caller's `TELEMETRY_READ` permission is re-checked periodically, not only at connect). Subject to per-organization and per-user concurrent-connection caps — 429 Too Many Requests when exceeded, returned as an ordinary JSON response before any SSE headers are sent.",
        "operationId": "streamDeviceLogs",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Device UUID",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "severity",
            "required": false,
            "in": "query",
            "description": "Filter by severity level",
            "schema": {
              "type": "string",
              "enum": [
                "DEBUG",
                "INFO",
                "WARN",
                "ERROR"
              ]
            }
          },
          {
            "name": "source",
            "required": false,
            "in": "query",
            "description": "Filter by log source (container name, service, or \"system\")",
            "schema": {
              "example": "my-container",
              "type": "string"
            }
          },
          {
            "name": "q",
            "required": false,
            "in": "query",
            "description": "Full-text search against the log message body, same semantics as the paged endpoint.",
            "schema": {
              "example": "connection refused",
              "type": "string"
            }
          },
          {
            "name": "last-event-id",
            "required": true,
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "text/event-stream of device log events"
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          },
          "404": {
            "description": "Device not found"
          },
          "429": {
            "description": "Too many concurrent log stream connections for this organization or user"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Stream device logs (follow mode)",
        "tags": [
          "Device Telemetry"
        ]
      }
    },
    "/api/v1/fleet/health": {
      "get": {
        "description": "Returns aggregated health metrics across all devices in the organization.",
        "operationId": "getFleetHealth",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fleet health summary retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FleetHealthResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — caller lacks required permission"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get fleet health summary",
        "tags": [
          "Fleet Health"
        ]
      }
    },
    "/api/v1/realtime/ticket": {
      "post": {
        "description": "Returns a short-lived, single-use ticket bound to the caller and the resolved organization. Redeem it once as `wss://<host>/ws?ticket=<ticket>`. A ticket is consumed on redemption, so each connection attempt — including every reconnect — requires a freshly minted ticket.",
        "operationId": "createWebSocketTicket",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Ticket minted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsTicketResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — invalid or missing authentication"
          },
          "403": {
            "description": "Forbidden — no accessible organization context"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mint a WebSocket handshake ticket",
        "tags": [
          "Realtime"
        ]
      }
    },
    "/api/v1/alert-rules": {
      "get": {
        "description": "Lists every alert rule in the caller’s organization, newest first.",
        "operationId": "listAlertRules",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AlertRuleResponseDto"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List alert rules",
        "tags": [
          "Alerts"
        ]
      },
      "post": {
        "description": "The expression is validated against the alert DSL at write time — a malformed rule is rejected here rather than silently never firing.",
        "operationId": "createAlertRule",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAlertRuleDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertRuleResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Malformed expression, or scope/target mismatch"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create an alert rule",
        "tags": [
          "Alerts"
        ]
      }
    },
    "/api/v1/alert-rules/deliveries": {
      "get": {
        "description": "Alert occurrences with their fired/resolved timestamps and delivery outcome, newest first. Optionally filtered by rule or device.",
        "operationId": "listAlertDeliveries",
        "parameters": [
          {
            "name": "ruleId",
            "required": false,
            "in": "query",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "deviceId",
            "required": false,
            "in": "query",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Default 50, maximum 200",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertDeliveryListResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List alert delivery history",
        "tags": [
          "Alerts"
        ]
      }
    },
    "/api/v1/alert-rules/metric-catalog": {
      "get": {
        "description": "Every field an alert expression may name, with its value type, unit, admitted operators and allowed values, plus one entry per device label key in the caller's organization. The builder in the web UI composes expressions from this list.",
        "operationId": "getAlertMetricCatalog",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertMetricCatalogResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get the alert expression field catalog",
        "tags": [
          "Alerts"
        ]
      }
    },
    "/api/v1/alert-rules/{id}": {
      "get": {
        "operationId": "getAlertRule",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertRuleResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "No such rule in the caller’s organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get an alert rule",
        "tags": [
          "Alerts"
        ]
      },
      "patch": {
        "operationId": "updateAlertRule",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAlertRuleDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertRuleResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "No such rule in the caller’s organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update an alert rule",
        "tags": [
          "Alerts"
        ]
      },
      "delete": {
        "description": "Cascades to the rule’s target wiring and its delivery history.",
        "operationId": "deleteAlertRule",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": ""
          },
          "404": {
            "description": "No such rule in the caller’s organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete an alert rule",
        "tags": [
          "Alerts"
        ]
      }
    },
    "/api/v1/alert-rules/{id}/test-fire": {
      "post": {
        "description": "Dispatches a test notification through each configured target so an operator can confirm delivery actually works before relying on it. Creates no AlertDelivery record, so a test never consumes the rule’s open-alert slot and cannot suppress a subsequent real alert. Per-target outcomes are reported individually — one broken endpoint does not hide the rest.",
        "operationId": "testFireAlertRule",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestFireResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "No such rule in the caller’s organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Send a test notification to a rule’s targets",
        "tags": [
          "Alerts"
        ]
      }
    },
    "/api/v1/notification-targets": {
      "get": {
        "description": "Lists every notification target in the caller’s organization. Webhook signing secrets are never included — only a boolean indicating whether one is configured.",
        "operationId": "listNotificationTargets",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NotificationTargetResponseDto"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "List notification targets",
        "tags": [
          "Alerts"
        ]
      },
      "post": {
        "description": "A WEBHOOK target is assigned a server-generated HMAC-SHA256 signing secret. That secret is returned in THIS response only and is never readable again; replace it with POST /:id/rotate-secret. A client cannot choose the secret (FM-1660).",
        "operationId": "createNotificationTarget",
        "parameters": [
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateNotificationTargetDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedNotificationTargetResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Missing channel-specific field, or a webhook URL pointing at a private, loopback, link-local or in-cluster address"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Create a notification target",
        "tags": [
          "Alerts"
        ]
      }
    },
    "/api/v1/notification-targets/{id}": {
      "get": {
        "operationId": "getNotificationTarget",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationTargetResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "No such target in the caller’s organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Get a notification target",
        "tags": [
          "Alerts"
        ]
      },
      "patch": {
        "description": "The channel is immutable — switching transports would leave the stored config mismatched and would retroactively mislabel every delivery already recorded against the target.",
        "operationId": "updateNotificationTarget",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateNotificationTargetDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationTargetResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "No such target in the caller’s organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Update a notification target",
        "tags": [
          "Alerts"
        ]
      },
      "delete": {
        "description": "Cascades to the target’s wiring on every alert rule and to its delivery history.",
        "operationId": "deleteNotificationTarget",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": ""
          },
          "404": {
            "description": "No such target in the caller’s organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Delete a notification target",
        "tags": [
          "Alerts"
        ]
      }
    },
    "/api/v1/notification-targets/{id}/rotate-secret": {
      "post": {
        "description": "Generates a new HMAC-SHA256 signing secret on the server and returns it in THIS response only. The previous secret stops working immediately (FM-1660).",
        "operationId": "rotateNotificationTargetSecret",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "name": "x-organization-id",
            "in": "header",
            "required": false,
            "description": "UUID of the organization (tenant) the request acts in. It selects the tenant and grants nothing: the server checks membership or an open tenant switch. Not needed for global operations. A malformed value returns 400. API keys are fixed to their own organization; another organization returns 403. The x-organization-source header grants nothing.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RotatedNotificationTargetSecretDto"
                }
              }
            }
          },
          "400": {
            "description": "The target is not a WEBHOOK target"
          },
          "404": {
            "description": "No such target in the caller’s organization"
          }
        },
        "security": [
          {
            "api-key": []
          },
          {
            "bearer": []
          }
        ],
        "summary": "Rotate the signing secret of a webhook target",
        "tags": [
          "Alerts"
        ]
      }
    }
  },
  "info": {
    "title": "logiccloud orchestrate API",
    "description": "\n## Overview\nlogiccloud orchestrate REST API for managing edge device fleets and workload deployments. logiccloud orchestrate implements the Workload Fleet Manager (WFM) role of the Margo specification.\n\n## Authentication\nAll API endpoints require authentication via Bearer token or API key.\n\n### Bearer Token\n```\nAuthorization: Bearer <jwt_token>\n```\n\n### API Key\n```\nX-API-Key: <api_key>\n```\n\n## Tenant scoping\nThe API is multi-tenant. Every endpoint acts in exactly one organization (tenant) unless it is marked as a global operation.\n\n### Selecting the tenant\nSend the `x-organization-id` header with the UUID of the organization. This header is the contract for API clients. It selects the tenant and grants nothing: the server still checks that you may act there. A malformed value returns 400. If you send no header, the server uses your open tenant switch, or else your primary organization. A route that names an organization in its path or body uses that organization, and a conflicting header is rejected with 403.\n\nThe web UI uses organization slugs in its page URLs. That is a web UI concept. The API only accepts the organization id. The `x-organization-source` header grants nothing: a forged value never bypasses any check.\n\n### Global operations\nSome endpoints are not scoped to one tenant and need no `x-organization-id`: the `/api/v1/tenant/*` context and switch endpoints, the platform-level organization lifecycle and record endpoints under `/api/v1/organizations` (superadmin), platform-wide activity and reporting endpoints under `/api/v1/activity`, and authentication event ingestion. All other endpoints are tenant-scoped.\n\n### Home tenants and tenant switches\nA home tenant is any tenant you are a member of. You act in a home tenant without a switch. `POST /api/v1/tenant/switch` to a home tenant opens no switch, ends any open switch and returns 204.\n\nA superadmin, or a user with the SUPPORT system role, who acts in a tenant they are not a member of needs an open tenant switch. Open one with `POST /api/v1/tenant/switch` and a `reason` (3-500 characters after trimming, else 400). The response is 201. A switch applies to the user's own requests only.\n\n### Switch lifetime\nA switch lasts 8 hours by default. You can choose an `expiresAt` up to 72 hours from now when you open it, and you can extend it with `PATCH /api/v1/tenant/switch` (new expiry at most 72 hours from now). A switch ends at its expiry (checked every 5 minutes), with `DELETE /api/v1/tenant/switch`, or when the user logs out of the web UI. CLI and SDK clients do not log out through the web UI, so the 8 hour default is their backstop: end the switch explicitly.\n\n### Tenant rejections (403)\n- No open switch: a superadmin or SUPPORT user targets a tenant they are not a member of. The body is `{\"statusCode\": 403, \"error\": \"Forbidden\", \"code\": \"TENANT_SWITCH_REQUIRED\", \"message\": \"A tenant switch is required to act in this organization\", \"organizationId\": \"<tenant>\"}`. Open a switch, then repeat the request. Schema: `TenantSwitchRequiredErrorDto`. Global operations never return it.\n- Not a member: a user without a system role targets a tenant they are not a member of. The body is the standard error body with the message `Not a member of this organization`.\n- The organization does not exist, or is inactive: 403 with the message `Organization not found` or `Organization is inactive`.\n- The `x-organization-id` header and the organization in the path differ: 403.\n- Insufficient permission in the tenant: 403.\n\n### API keys\nAn API key is fixed to the organization it was issued for. It never uses a tenant switch, not even one of the user who created it. A header or path naming another organization returns 403 (`API key not authorized for this organization`). Every `/api/v1/tenant/switch*` endpoint, and the other tenant context endpoints under `/api/v1/tenant`, returns 403 for an API key.\n\n## Rate Limiting\n- Default: 100 requests per minute\n- Burst: 20 requests per second\n\n## Pagination\nList endpoints support pagination via `page` and `pageSize` query parameters:\n- `page`: Page number (default: 1)\n- `pageSize`: Items per page (default: 20, max: 100)\n\n## Filtering\nMost list endpoints support filtering via query parameters specific to the resource.\n\n## Error Responses\nErrors use the standard NestJS body: `{\"statusCode\": <status>, \"message\": <text, or a list of texts for validation errors>, \"error\": \"<status name>\"}`. Some errors add a machine-readable `code` and context fields, for example `TENANT_SWITCH_REQUIRED` above. The body does not follow RFC 7807 Problem Details.\n    ",
    "version": "2.105.2",
    "contact": {
      "name": "logiccloud orchestrate team",
      "url": "https://github.com/fleet-manager",
      "email": "support@fleet-manager.io"
    },
    "license": {
      "name": "Apache 2.0",
      "url": "https://www.apache.org/licenses/LICENSE-2.0"
    }
  },
  "tags": [
    {
      "name": "Devices",
      "description": "Device management operations"
    },
    {
      "name": "Device Certificates",
      "description": "Device certificate management"
    },
    {
      "name": "Certificates",
      "description": "Certificate overview and statistics"
    },
    {
      "name": "CA Certificates",
      "description": "Certificate Authority (CA) management"
    },
    {
      "name": "Groups",
      "description": "Device group management"
    },
    {
      "name": "Organizations",
      "description": "Organization management"
    },
    {
      "name": "Users",
      "description": "User management"
    },
    {
      "name": "Organization Members",
      "description": "Organization member management"
    },
    {
      "name": "Roles",
      "description": "Role-based access control"
    },
    {
      "name": "API Keys",
      "description": "API key management"
    },
    {
      "name": "Invitations",
      "description": "User invitation management"
    },
    {
      "name": "Public Invitations",
      "description": "Public invitation endpoints (no authentication required)"
    },
    {
      "name": "Audit",
      "description": "Audit log operations"
    }
  ],
  "servers": [
    {
      "url": "http://localhost:3000",
      "description": "Development"
    },
    {
      "url": "https://api.fleet-manager.io",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http",
        "description": "JWT authentication token"
      },
      "api-key": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key for service-to-service communication"
      }
    },
    "schemas": {
      "HealthCheckResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "healthy"
          },
          "service": {
            "type": "string",
            "example": "api-gateway"
          },
          "timestamp": {
            "type": "string",
            "example": "2024-01-15T10:30:00.000Z"
          }
        },
        "required": [
          "status",
          "service",
          "timestamp"
        ]
      },
      "LivenessResponse": {
        "type": "object",
        "properties": {
          "live": {
            "type": "boolean",
            "example": true
          }
        },
        "required": [
          "live"
        ]
      },
      "FederationSyncResponseDto": {
        "type": "object",
        "properties": {
          "usersProcessed": {
            "type": "number",
            "description": "Number of brokered Keycloak users mapped to a logiccloud orchestrate user and reconciled",
            "example": 42
          },
          "added": {
            "type": "number",
            "description": "Total FEDERATED memberships created",
            "example": 3
          },
          "updated": {
            "type": "number",
            "description": "Total memberships whose role changed",
            "example": 1
          },
          "removed": {
            "type": "number",
            "description": "Total FEDERATED memberships removed",
            "example": 2
          },
          "skipped": {
            "type": "number",
            "description": "Number of Keycloak users skipped (no WFM user, no Entra attributes, or a per-user error)",
            "example": 5
          }
        },
        "required": [
          "usersProcessed",
          "added",
          "updated",
          "removed",
          "skipped"
        ]
      },
      "FederatedGroupMappingResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique mapping identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization the mapping grants membership in",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "provider": {
            "type": "string",
            "description": "Identity provider that supplies the app-role / group",
            "enum": [
              "ENTRA",
              "LDAP"
            ],
            "example": "ENTRA"
          },
          "externalId": {
            "type": "string",
            "description": "Provider role value or group id carried in the token claim",
            "example": "fleet-operators"
          },
          "externalKind": {
            "type": "string",
            "description": "Kind of external identifier (application role or group)",
            "enum": [
              "APP_ROLE",
              "GROUP"
            ],
            "example": "GROUP"
          },
          "externalLabel": {
            "type": "string",
            "description": "Human-readable label captured at mapping time",
            "example": "Fleet Operators (Entra group)"
          },
          "roleId": {
            "type": "string",
            "description": "logiccloud orchestrate role granted to users matching this mapping",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the mapping is active (resolved at login time)",
            "example": true
          },
          "createdById": {
            "type": "string",
            "description": "Id of the user who created the mapping",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440003"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the mapping was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the mapping was last updated",
            "example": "2024-06-20T14:45:00Z"
          }
        },
        "required": [
          "id",
          "organizationId",
          "provider",
          "externalId",
          "externalKind",
          "roleId",
          "enabled",
          "createdAt",
          "updatedAt"
        ]
      },
      "FederatedGroupMappingListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of federated group mappings",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FederatedGroupMappingResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of mappings matching the query",
            "example": 3
          }
        },
        "required": [
          "items",
          "total"
        ]
      },
      "CreateFederatedGroupMappingDto": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Identity provider that supplies the app-role / group",
            "enum": [
              "ENTRA",
              "LDAP"
            ],
            "example": "ENTRA"
          },
          "externalId": {
            "type": "string",
            "description": "Provider role value or group id carried in the token claim",
            "example": "fleet-operators",
            "maxLength": 255
          },
          "externalKind": {
            "type": "string",
            "description": "Kind of external identifier (application role or group)",
            "enum": [
              "APP_ROLE",
              "GROUP"
            ],
            "example": "GROUP"
          },
          "externalLabel": {
            "type": "string",
            "description": "Human-readable label captured at mapping time",
            "example": "Fleet Operators (Entra group)",
            "maxLength": 255
          },
          "organizationId": {
            "type": "string",
            "description": "Organization the mapping grants membership in",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "roleId": {
            "type": "string",
            "description": "logiccloud orchestrate role granted to users matching this mapping",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the mapping is active (resolved at login time)",
            "default": true,
            "example": true
          }
        },
        "required": [
          "provider",
          "externalId",
          "externalKind",
          "organizationId",
          "roleId"
        ]
      },
      "UpdateFederatedGroupMappingDto": {
        "type": "object",
        "properties": {
          "externalId": {
            "type": "string",
            "description": "Provider role value or group id carried in the token claim",
            "example": "fleet-operators",
            "maxLength": 255
          },
          "externalKind": {
            "type": "string",
            "description": "Kind of external identifier (application role or group)",
            "enum": [
              "APP_ROLE",
              "GROUP"
            ],
            "example": "GROUP"
          },
          "externalLabel": {
            "type": "string",
            "description": "Human-readable label captured at mapping time",
            "example": "Fleet Operators (Entra group)",
            "maxLength": 255
          },
          "roleId": {
            "type": "string",
            "description": "logiccloud orchestrate role granted to users matching this mapping",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the mapping is active (resolved at login time)",
            "default": true,
            "example": true
          }
        }
      },
      "DeviceResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique device identifier (UUID)",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "format": "uuid"
          },
          "clientId": {
            "type": "string",
            "description": "Client-provided device identifier",
            "example": "edge-device-001"
          },
          "name": {
            "type": "string",
            "description": "Human-readable device name",
            "example": "Production Edge Gateway 1"
          },
          "status": {
            "type": "string",
            "description": "Current device status",
            "example": "ONLINE",
            "enum": [
              "ONLINE",
              "OFFLINE",
              "PENDING",
              "ERROR"
            ]
          },
          "capabilities": {
            "type": "object",
            "description": "Device capabilities as reported during onboarding",
            "example": {
              "cpu": {
                "cores": 4,
                "architecture": "arm64"
              },
              "memory": {
                "total": 8192
              },
              "containers": {
                "runtime": "containerd"
              }
            },
            "additionalProperties": true
          },
          "labels": {
            "type": "object",
            "description": "Key-value labels for device organization and filtering",
            "example": {
              "environment": "production",
              "region": "us-west",
              "tier": "edge"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "lastSeenAt": {
            "format": "date-time",
            "type": "string",
            "description": "Timestamp of last communication from device",
            "example": "2024-01-15T10:30:00Z"
          },
          "deletionRequestedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Set when deletion was requested (FM-1359). The device stays listed while its deployments are withdrawn; it is deleted once the removals are confirmed or time out.",
            "example": "2024-01-15T10:30:00Z"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Timestamp when device was registered",
            "example": "2024-01-01T00:00:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Timestamp of last device update",
            "example": "2024-01-15T10:30:00Z"
          },
          "reportedDeviceId": {
            "type": "string",
            "description": "Margo self-reported DeviceId (properties.id from the device capabilities manifest). Unique per organization among non-deleted devices — null if this device has never reported capabilities, or if it collided with another device already holding the same value (FM-1043) and its write was rejected.",
            "example": "wm-uc20-m3000-ax4b14pc7500001"
          },
          "cpuUsagePercent": {
            "type": "number",
            "description": "Latest reported CPU usage percentage (FM-770, from VictoriaMetrics). Null if the device has never reported telemetry.",
            "example": 42.5,
            "nullable": true
          },
          "memoryUsagePercent": {
            "type": "number",
            "description": "Latest reported memory usage percentage (FM-770, from VictoriaMetrics). Null if the device has never reported telemetry.",
            "example": 67.2,
            "nullable": true
          },
          "diskUsagePercent": {
            "type": "number",
            "description": "Latest reported disk usage percentage (FM-770, from VictoriaMetrics). Null if the device has never reported telemetry.",
            "example": 55.1,
            "nullable": true
          },
          "runningWorkloadCount": {
            "type": "number",
            "description": "Count of non-removed deployment instances currently targeting this device (FM-770). Always a real count, independent of telemetry availability.",
            "example": 3,
            "nullable": true
          }
        },
        "required": [
          "id",
          "clientId",
          "status",
          "capabilities",
          "labels",
          "createdAt",
          "updatedAt"
        ]
      },
      "DeviceListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "Array of device objects",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of devices matching the filter criteria",
            "example": 150,
            "minimum": 0
          },
          "hasMore": {
            "type": "boolean",
            "description": "Indicates if there are more devices available",
            "example": true
          },
          "nextCursor": {
            "type": "string",
            "description": "Cursor for fetching the next page of results",
            "example": "eyJpZCI6IjEyMzQifQ=="
          }
        },
        "required": [
          "data",
          "total",
          "hasMore"
        ]
      },
      "RecentActivityItemDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique event ID"
          },
          "type": {
            "type": "string",
            "description": "Event type",
            "enum": [
              "deployment.status",
              "device.registered",
              "certificate.issued",
              "registry-sync-failed"
            ]
          },
          "message": {
            "type": "string",
            "description": "Human-readable event message"
          },
          "timestamp": {
            "format": "date-time",
            "type": "string",
            "description": "When the event occurred"
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata about the event"
          }
        },
        "required": [
          "id",
          "type",
          "message",
          "timestamp"
        ]
      },
      "RecentActivityResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Recent activity items",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecentActivityItemDto"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "UpdateDeviceDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable device name",
            "example": "Production Edge Gateway 1",
            "maxLength": 255
          },
          "labels": {
            "type": "object",
            "description": "Key-value labels for device organization and filtering",
            "example": {
              "environment": "production",
              "region": "us-west",
              "tier": "edge"
            },
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "DeviceCertificateResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique certificate identifier (UUID)",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "format": "uuid"
          },
          "deviceId": {
            "type": "string",
            "description": "Device ID this certificate belongs to",
            "example": "550e8400-e29b-41d4-a716-446655440001",
            "format": "uuid"
          },
          "fingerprint": {
            "type": "string",
            "description": "SHA-256 fingerprint of the certificate",
            "example": "A1:B2:C3:D4:E5:F6:A1:B2:C3:D4:E5:F6:A1:B2:C3:D4:E5:F6:A1:B2:C3:D4:E5:F6:A1:B2:C3:D4:E5:F6:A1:B2"
          },
          "subject": {
            "type": "string",
            "description": "Certificate subject (Distinguished Name)",
            "example": "CN=edge-device-001,O=Margo WFM,OU=Devices"
          },
          "issuer": {
            "type": "string",
            "description": "Certificate issuer (Distinguished Name)",
            "example": "CN=Margo Root CA,O=Margo WFM,OU=Certificate Authority"
          },
          "notBefore": {
            "format": "date-time",
            "type": "string",
            "description": "Certificate validity start date",
            "example": "2024-01-01T00:00:00Z"
          },
          "notAfter": {
            "format": "date-time",
            "type": "string",
            "description": "Certificate validity end date (expiration)",
            "example": "2025-01-01T00:00:00Z"
          },
          "validityKnown": {
            "type": "boolean",
            "description": "False for a legacy certificate record whose notBefore, notAfter and issuer were not read from a certificate (stored before the presented certificate was kept). Its dates are placeholders. The record refreshes when the device re-onboards.",
            "example": true
          },
          "revoked": {
            "type": "boolean",
            "description": "Whether the certificate has been revoked",
            "example": false
          },
          "revokedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Date when the certificate was revoked",
            "example": "2024-06-01T00:00:00Z"
          },
          "revokeReason": {
            "type": "string",
            "description": "Reason for certificate revocation",
            "example": "Key compromise",
            "enum": [
              "Key compromise",
              "CA compromise",
              "Affiliation changed",
              "Superseded",
              "Cessation of operation",
              "Certificate hold",
              "Other"
            ]
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Timestamp when the certificate was issued",
            "example": "2024-01-01T00:00:00Z"
          }
        },
        "required": [
          "id",
          "deviceId",
          "fingerprint",
          "subject",
          "issuer",
          "notBefore",
          "notAfter",
          "validityKnown",
          "revoked",
          "createdAt"
        ]
      },
      "DeviceCertificateListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "List of device certificates",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceCertificateResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of certificates for this device",
            "example": 3
          }
        },
        "required": [
          "data",
          "total"
        ]
      },
      "RevokeCertificateDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for revoking the certificate",
            "enum": [
              "Key compromise",
              "CA compromise",
              "Affiliation changed",
              "Superseded",
              "Cessation of operation",
              "Certificate hold",
              "Other"
            ],
            "example": "Key compromise"
          },
          "notes": {
            "type": "string",
            "description": "Additional notes about the revocation (required if reason is \"Other\")",
            "example": "Device was stolen from warehouse",
            "maxLength": 1000
          }
        },
        "required": [
          "reason"
        ]
      },
      "RenewCertificateDto": {
        "type": "object",
        "properties": {
          "validityDays": {
            "type": "number",
            "description": "Ignored. Device certificate renewal is disabled and always fails with 409 (FM-1318).",
            "example": 365,
            "minimum": 1,
            "maximum": 3650,
            "default": 365
          }
        }
      },
      "PreviewDeploymentTargetsDto": {
        "type": "object",
        "properties": {
          "deviceIds": {
            "description": "Devices selected directly",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "deviceGroupIds": {
            "description": "Device groups to target",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "deviceSelector": {
            "type": "object",
            "description": "Label selector (matchLabels / matchExpressions)",
            "additionalProperties": true
          },
          "limit": {
            "type": "number",
            "description": "Maximum number of devices listed",
            "default": 500
          }
        }
      },
      "DeviceGroupRefDto": {
        "type": "object",
        "properties": {
          "labelKey": {
            "type": "string",
            "description": "Label key that defines the group",
            "example": "site"
          },
          "labelValue": {
            "type": "string",
            "description": "Label value that defines the group",
            "example": "berlin"
          }
        },
        "required": [
          "labelKey",
          "labelValue"
        ]
      },
      "RemoveDeploymentTargetsDto": {
        "type": "object",
        "properties": {
          "deviceIds": {
            "description": "Devices to remove the deployment from. Mutually exclusive with `group`.",
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "group": {
            "description": "Device group (label key/value). Its members are resolved once, at request time. Mutually exclusive with `deviceIds`.",
            "allOf": [
              {
                "$ref": "#/components/schemas/DeviceGroupRefDto"
              }
            ]
          }
        }
      },
      "RemoveDeploymentTargetsResultDto": {
        "type": "object",
        "properties": {
          "deploymentRemoved": {
            "type": "boolean",
            "description": "True when the request covered every remaining target, so the whole deployment was removed (the same flow as DELETE /deployments/:id). False when the deployment stays live."
          },
          "removedDeviceIds": {
            "description": "Devices the removal was requested for (group members resolved at request time)",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "remainingTargets": {
            "type": "number",
            "description": "Devices that remain targets of the deployment after this request"
          }
        },
        "required": [
          "deploymentRemoved",
          "removedDeviceIds",
          "remainingTargets"
        ]
      },
      "SplitDeploymentTargetsDto": {
        "type": "object",
        "properties": {
          "deviceIds": {
            "description": "Current targets to split off into their own deployments",
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        },
        "required": [
          "deviceIds"
        ]
      },
      "UpdateDeviceDeploymentDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "parameterOverrides": {
            "type": "object",
            "additionalProperties": true
          },
          "targetNamespace": {
            "type": "string",
            "nullable": true,
            "description": "null clears the override"
          },
          "desiredState": {
            "type": "string",
            "enum": [
              "PENDING",
              "INSTALLING",
              "INSTALLED",
              "FAILED",
              "REMOVING",
              "REMOVED"
            ]
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "RedeployDeploymentDto": {
        "type": "object",
        "properties": {
          "applicationDescriptionId": {
            "type": "string",
            "description": "Target application version (ApplicationDescription id) of the same application",
            "format": "uuid"
          },
          "parameterOverrides": {
            "type": "object",
            "description": "The complete set of parameter overrides of the new revision (full replacement)",
            "additionalProperties": true
          }
        },
        "required": [
          "applicationDescriptionId",
          "parameterOverrides"
        ]
      },
      "RedeployRevisionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "object",
            "format": "uuid",
            "nullable": true
          },
          "revision": {
            "type": "object",
            "nullable": true,
            "description": "Monotonic revision number of the deployment"
          },
          "applicationDescriptionId": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "string",
            "description": "Application version string"
          },
          "parameterDigest": {
            "type": "string",
            "description": "sha256 of the canonical JSON of the parameter overrides"
          }
        },
        "required": [
          "id",
          "revision",
          "applicationDescriptionId",
          "version",
          "parameterDigest"
        ]
      },
      "RedeployDeploymentResultDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The deployment that was re-deployed (unchanged)"
          },
          "name": {
            "type": "string"
          },
          "fromRevision": {
            "$ref": "#/components/schemas/RedeployRevisionDto"
          },
          "toRevision": {
            "$ref": "#/components/schemas/RedeployRevisionDto"
          },
          "deploymentIds": {
            "description": "Every deployment that moved in the same transaction (the deployment group)",
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "resetInstances": {
            "type": "number",
            "description": "Number of device instances reset to PENDING"
          },
          "released": {
            "type": "boolean",
            "description": "True when the new revision was released to the devices at once (IMMEDIATE strategy). A staged strategy releases it batch by batch."
          },
          "deploymentGroupId": {
            "type": "string",
            "format": "uuid",
            "description": "FM-1514: the deployment group, when the re-deploy addressed the group"
          }
        },
        "required": [
          "id",
          "name",
          "fromRevision",
          "toRevision",
          "deploymentIds",
          "resetInstances",
          "released"
        ]
      },
      "IncludeDeploymentTargetsDto": {
        "type": "object",
        "properties": {
          "deviceIds": {
            "description": "Previously removed devices to make ordinary targets of the deployment again",
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        },
        "required": [
          "deviceIds"
        ]
      },
      "IncludeDeploymentTargetsResultDto": {
        "type": "object",
        "properties": {
          "includedDeviceIds": {
            "description": "Devices that are ordinary targets again",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "includedDeviceIds"
        ]
      },
      "ConfirmDeploymentRemovalDto": {
        "type": "object",
        "properties": {
          "deviceIds": {
            "description": "Devices on which the operator confirms the workload is removed",
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "note": {
            "type": "string",
            "description": "Why the operator knows the workload is gone. Stored in the status history.",
            "maxLength": 500
          }
        },
        "required": [
          "deviceIds"
        ]
      },
      "ConfirmDeploymentRemovalResultDto": {
        "type": "object",
        "properties": {
          "confirmedDeviceIds": {
            "description": "Devices settled as removed by this request. A device that confirmed on its own while the request ran is not listed.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "remainingAwaiting": {
            "type": "number",
            "description": "Devices of this deployment that still await removal confirmation"
          }
        },
        "required": [
          "confirmedDeviceIds",
          "remainingAwaiting"
        ]
      },
      "TriggerRollbackDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Operator-supplied reason for the rollback, recorded in the audit trail",
            "maxLength": 500,
            "example": "Canary devices reported repeated container crashes"
          }
        }
      },
      "DeviceSelectorExpressionDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Label key to match on",
            "example": "line"
          },
          "operator": {
            "type": "string",
            "enum": [
              "In",
              "NotIn",
              "Exists",
              "DoesNotExist"
            ],
            "example": "In"
          },
          "values": {
            "example": [
              "line-3"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "key",
          "operator"
        ]
      },
      "DeviceSelectorDto": {
        "type": "object",
        "properties": {
          "matchLabels": {
            "type": "object",
            "description": "Exact label equality match; all entries must match (AND).",
            "example": {
              "site": "plant-a"
            }
          },
          "matchExpressions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceSelectorExpressionDto"
            }
          }
        }
      },
      "RollbackDevicesBySelectorDto": {
        "type": "object",
        "properties": {
          "deviceSelector": {
            "description": "Label selector choosing which devices to roll back",
            "allOf": [
              {
                "$ref": "#/components/schemas/DeviceSelectorDto"
              }
            ]
          },
          "reason": {
            "type": "string",
            "description": "Operator-supplied reason, recorded on the audit entry",
            "maxLength": 500
          },
          "applicationPackageId": {
            "type": "string",
            "description": "Which application package to roll back on every matching device.",
            "format": "uuid"
          }
        },
        "required": [
          "deviceSelector"
        ]
      },
      "RollbackDeviceVersionDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Operator-supplied reason, recorded on the audit entry",
            "maxLength": 500,
            "example": "v2.4.1 crash-looping on the line-3 gateways"
          },
          "applicationPackageId": {
            "type": "string",
            "description": "Which application to roll back. Rollback is scoped to one application package; on a device running several, omitting this picks the most recently sealed rollbackable one.",
            "format": "uuid"
          }
        }
      },
      "StatusBreakdownDto": {
        "type": "object",
        "properties": {
          "online": {
            "type": "number",
            "description": "Number of online devices",
            "example": 45
          },
          "offline": {
            "type": "number",
            "description": "Number of offline devices",
            "example": 5
          },
          "pending": {
            "type": "number",
            "description": "Number of pending devices",
            "example": 2
          },
          "error": {
            "type": "number",
            "description": "Number of devices in error state",
            "example": 1
          }
        },
        "required": [
          "online",
          "offline",
          "pending",
          "error"
        ]
      },
      "FleetStatsResponseDto": {
        "type": "object",
        "properties": {
          "totalDevices": {
            "type": "number",
            "description": "Total number of devices in the fleet",
            "example": 150
          },
          "byStatus": {
            "description": "Status breakdown of all devices",
            "allOf": [
              {
                "$ref": "#/components/schemas/StatusBreakdownDto"
              }
            ]
          },
          "labelKeys": {
            "description": "List of all label keys used in the organization",
            "example": [
              "environment",
              "region",
              "tier"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "unlabeledDeviceCount": {
            "type": "number",
            "description": "Number of devices with no labels assigned",
            "example": 3
          }
        },
        "required": [
          "totalDevices",
          "byStatus",
          "labelKeys",
          "unlabeledDeviceCount"
        ]
      },
      "LabelKeysResponseDto": {
        "type": "object",
        "properties": {
          "keys": {
            "description": "List of unique label keys used in the organization",
            "example": [
              "environment",
              "region",
              "tier",
              "version"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "keys"
        ]
      },
      "LabelValuesResponseDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "The label key",
            "example": "environment"
          },
          "values": {
            "description": "List of unique values for this label key",
            "example": [
              "production",
              "staging",
              "development"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "key",
          "values"
        ]
      },
      "LabelGroupDto": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string",
            "description": "Label value",
            "example": "production"
          },
          "count": {
            "type": "number",
            "description": "Number of devices with this label value",
            "example": 25
          },
          "byStatus": {
            "description": "Status breakdown for this group",
            "allOf": [
              {
                "$ref": "#/components/schemas/StatusBreakdownDto"
              }
            ]
          }
        },
        "required": [
          "value",
          "count",
          "byStatus"
        ]
      },
      "GroupStatsByLabelResponseDto": {
        "type": "object",
        "properties": {
          "labelKey": {
            "type": "string",
            "description": "The label key used for grouping",
            "example": "environment"
          },
          "groups": {
            "description": "Statistics for each label value",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LabelGroupDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of devices across all groups",
            "example": 53
          }
        },
        "required": [
          "labelKey",
          "groups",
          "total"
        ]
      },
      "QueryGroupStatsDto": {
        "type": "object",
        "properties": {
          "labelSelector": {
            "type": "object",
            "description": "Label selector: exact key/value pairs, all of which must match (AND). 1-20 entries, keys 1-253 and values up to 253 characters, string values only, no control characters. An empty selector is rejected with 400; use GET /api/v1/groups/stats for the whole fleet.",
            "example": {
              "environment": "production",
              "region": "us-west"
            },
            "additionalProperties": {
              "type": "string",
              "maxLength": 253
            },
            "minProperties": 1,
            "maxProperties": 20
          }
        },
        "required": [
          "labelSelector"
        ]
      },
      "GroupStatsResponseDto": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Total number of matching devices",
            "example": 25
          },
          "byStatus": {
            "description": "Status breakdown of matching devices",
            "allOf": [
              {
                "$ref": "#/components/schemas/StatusBreakdownDto"
              }
            ]
          },
          "labelSelector": {
            "type": "object",
            "description": "The label selector used for filtering",
            "example": {
              "environment": "production"
            },
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "required": [
          "total",
          "byStatus"
        ]
      },
      "AddLabelToGroupDto": {
        "type": "object",
        "properties": {
          "labelSelector": {
            "type": "object",
            "description": "Label selector to match devices",
            "example": {
              "environment": "production",
              "region": "us-west"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "key": {
            "type": "string",
            "description": "Label key to add",
            "example": "tier"
          },
          "value": {
            "type": "string",
            "description": "Label value to add",
            "example": "edge"
          }
        },
        "required": [
          "labelSelector",
          "key",
          "value"
        ]
      },
      "BulkOperationResponseDto": {
        "type": "object",
        "properties": {
          "affected": {
            "type": "number",
            "description": "Number of devices affected by the operation",
            "example": 15
          },
          "deviceIds": {
            "description": "IDs of affected devices",
            "example": [
              "550e8400-e29b-41d4-a716-446655440000",
              "550e8400-e29b-41d4-a716-446655440001"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "affected",
          "deviceIds"
        ]
      },
      "RemoveLabelFromGroupDto": {
        "type": "object",
        "properties": {
          "labelSelector": {
            "type": "object",
            "description": "Label selector to match devices",
            "example": {
              "environment": "production",
              "region": "us-west"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "key": {
            "type": "string",
            "description": "Label key to remove",
            "example": "tier"
          }
        },
        "required": [
          "labelSelector",
          "key"
        ]
      },
      "BulkUpdateLabelsDto": {
        "type": "object",
        "properties": {
          "labelSelector": {
            "type": "object",
            "description": "Label selector to match devices",
            "example": {
              "environment": "staging"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "labels": {
            "type": "object",
            "description": "Labels to add or update on matching devices",
            "example": {
              "tier": "production",
              "version": "v2.0"
            },
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "required": [
          "labelSelector",
          "labels"
        ]
      },
      "RenameLabelKeyDto": {
        "type": "object",
        "properties": {
          "newKey": {
            "type": "string",
            "description": "The new label key. Must not already be carried by any device of the organization, nor referenced by any deployment selector or alert rule (409 otherwise).",
            "example": "site",
            "minLength": 1,
            "maxLength": 253
          }
        },
        "required": [
          "newKey"
        ]
      },
      "LabelRenameResponseDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "The label key that was renamed, or whose value was renamed"
          },
          "newKey": {
            "type": "string",
            "description": "Key rename only: the new label key",
            "example": "site"
          },
          "value": {
            "type": "string",
            "description": "Value rename only: the old value",
            "example": "Plant-A"
          },
          "newValue": {
            "type": "string",
            "description": "Value rename only: the new value",
            "example": "Plant-1"
          },
          "affectedDevices": {
            "type": "number",
            "description": "Number of devices whose labels were rewritten",
            "example": 42
          },
          "updatedDeploymentIds": {
            "description": "Deployments whose device selector referenced the renamed label and was rewritten to keep targeting the same devices",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "updatedAlertRuleIds": {
            "description": "Alert rules whose label target referenced the renamed label and was rewritten to keep matching the same devices",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "key",
          "affectedDevices",
          "updatedDeploymentIds",
          "updatedAlertRuleIds"
        ]
      },
      "RenameLabelValueDto": {
        "type": "object",
        "properties": {
          "newValue": {
            "type": "string",
            "description": "The new value for the label key. Must not already be carried under that key by any device of the organization, nor selected by any deployment selector or alert rule (409 otherwise).",
            "example": "Plant-1",
            "minLength": 1,
            "maxLength": 253
          }
        },
        "required": [
          "newValue"
        ]
      },
      "BulkDeleteGroupDto": {
        "type": "object",
        "properties": {
          "labelSelector": {
            "type": "object",
            "description": "Label selector to match devices for deletion",
            "example": {
              "environment": "development",
              "status": "deprecated"
            },
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "required": [
          "labelSelector"
        ]
      },
      "BulkDeleteResponseDto": {
        "type": "object",
        "properties": {
          "deleted": {
            "type": "number",
            "description": "Number of devices deleted",
            "example": 5
          },
          "deviceIds": {
            "description": "IDs of deleted devices",
            "example": [
              "550e8400-e29b-41d4-a716-446655440000"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "deleted",
          "deviceIds"
        ]
      },
      "CreateDeviceGroupDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Group name, unique among live groups of the organization (case-insensitive)",
            "minLength": 1,
            "maxLength": 100,
            "example": "Plant Berlin"
          },
          "description": {
            "type": "string",
            "description": "Free-text description",
            "maxLength": 1000
          }
        },
        "required": [
          "name"
        ]
      },
      "DeviceGroupResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "memberCount": {
            "type": "number",
            "description": "Number of live member devices"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "memberCount",
          "createdAt",
          "updatedAt"
        ]
      },
      "Object": {
        "type": "object",
        "properties": {}
      },
      "DeviceGroupListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceGroupResponseDto"
            }
          },
          "total": {
            "type": "number"
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "total",
          "hasMore"
        ]
      },
      "DeviceGroupMemberResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "clientId": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "labels": {
            "type": "object",
            "additionalProperties": true
          },
          "lastSeenAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "addedAt": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "clientId",
          "status",
          "labels",
          "addedAt"
        ]
      },
      "DeviceGroupMemberListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceGroupMemberResponseDto"
            }
          },
          "total": {
            "type": "number"
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "total",
          "hasMore"
        ]
      },
      "UpdateDeviceGroupDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "New group name",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "description": "New description; null clears it",
            "maxLength": 1000,
            "nullable": true
          }
        }
      },
      "DeviceGroupMembersDto": {
        "type": "object",
        "properties": {
          "deviceIds": {
            "description": "Devices to add or remove (1 to 1000, unique)",
            "minItems": 1,
            "maxItems": 1000,
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        },
        "required": [
          "deviceIds"
        ]
      },
      "AddDeviceGroupMembersResultDto": {
        "type": "object",
        "properties": {
          "added": {
            "type": "number",
            "description": "Devices newly added"
          },
          "alreadyMembers": {
            "type": "number",
            "description": "Devices that were members already (ignored)"
          }
        },
        "required": [
          "added",
          "alreadyMembers"
        ]
      },
      "RemoveDeviceGroupMembersResultDto": {
        "type": "object",
        "properties": {
          "removedDeviceIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "notMemberDeviceIds": {
            "description": "Devices that were not members (ignored)",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "splits": {
            "type": "array",
            "description": "Per device, the deployments split off from their source deployment",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "removedDeploymentIds": {
            "description": "Source deployments left without a target",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "removedDeviceIds",
          "notMemberDeviceIds",
          "splits",
          "removedDeploymentIds"
        ]
      },
      "CreateOrganizationDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Organization display name",
            "minLength": 2,
            "maxLength": 100,
            "example": "Acme Corporation"
          },
          "slug": {
            "type": "string",
            "description": "Unique URL-friendly identifier (lowercase alphanumeric with hyphens)",
            "minLength": 2,
            "maxLength": 50,
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
            "example": "acme-corp"
          },
          "description": {
            "type": "string",
            "description": "Organization description",
            "maxLength": 500,
            "example": "Leading manufacturer of industrial automation equipment"
          },
          "settings": {
            "type": "object",
            "description": "Organization-specific settings",
            "additionalProperties": true,
            "example": {
              "timezone": "America/New_York",
              "locale": "en-US"
            }
          },
          "branding": {
            "type": "object",
            "description": "Organization branding configuration",
            "additionalProperties": true,
            "example": {
              "primaryColor": "#003366",
              "logo": "https://example.com/logo.png"
            }
          },
          "primaryContact": {
            "type": "string",
            "description": "Primary contact name for the organization",
            "maxLength": 200,
            "example": "John Smith"
          },
          "billingEmail": {
            "type": "string",
            "description": "Billing email address for the organization",
            "format": "email",
            "example": "billing@acme-corp.com"
          }
        },
        "required": [
          "name",
          "slug"
        ]
      },
      "OrganizationStatsDto": {
        "type": "object",
        "properties": {
          "userCount": {
            "type": "number",
            "description": "Total number of users in the organization",
            "example": 25
          },
          "activeUsers": {
            "type": "number",
            "description": "Number of active users in the organization",
            "example": 20
          },
          "deviceCount": {
            "type": "number",
            "description": "Total number of devices in the organization",
            "example": 150
          },
          "activeDevices": {
            "type": "number",
            "description": "Number of online or provisioned devices",
            "example": 120
          },
          "deploymentCount": {
            "type": "number",
            "description": "Total number of deployments in the organization",
            "example": 42
          },
          "runningDeployments": {
            "type": "number",
            "description": "Number of running deployments",
            "example": 35
          }
        },
        "required": [
          "userCount",
          "activeUsers",
          "deviceCount",
          "activeDevices",
          "deploymentCount",
          "runningDeployments"
        ]
      },
      "OrganizationResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique organization identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "name": {
            "type": "string",
            "description": "Organization display name",
            "example": "Acme Corporation"
          },
          "slug": {
            "type": "string",
            "description": "Unique URL-friendly identifier",
            "example": "acme-corp"
          },
          "description": {
            "type": "string",
            "description": "Organization description",
            "example": "Leading manufacturer of industrial automation equipment"
          },
          "settings": {
            "type": "object",
            "description": "Organization-specific settings",
            "additionalProperties": true,
            "example": {
              "timezone": "America/New_York",
              "locale": "en-US"
            }
          },
          "branding": {
            "type": "object",
            "description": "Organization branding configuration",
            "additionalProperties": true,
            "example": {
              "primaryColor": "#003366",
              "logo": "https://example.com/logo.png"
            }
          },
          "status": {
            "type": "string",
            "description": "Organization status",
            "enum": [
              "active",
              "suspended",
              "pending",
              "disabled"
            ],
            "example": "active"
          },
          "lastActivityAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last activity timestamp",
            "example": "2024-06-20T14:45:00Z",
            "nullable": true
          },
          "primaryContact": {
            "type": "string",
            "description": "Primary contact name",
            "example": "John Smith",
            "nullable": true
          },
          "billingEmail": {
            "type": "string",
            "description": "Billing email address",
            "format": "email",
            "example": "billing@acme-corp.com",
            "nullable": true
          },
          "active": {
            "type": "boolean",
            "description": "Whether the organization is active (deprecated, use status)",
            "example": true,
            "deprecated": true
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the organization was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the organization was last updated",
            "example": "2024-06-20T14:45:00Z"
          },
          "stats": {
            "description": "Organization resource statistics",
            "allOf": [
              {
                "$ref": "#/components/schemas/OrganizationStatsDto"
              }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "settings",
          "branding",
          "status",
          "active",
          "createdAt",
          "updatedAt"
        ]
      },
      "OrganizationListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of organizations",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganizationResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of organizations matching the query",
            "example": 100
          },
          "limit": {
            "type": "number",
            "description": "Number of results returned",
            "example": 50
          },
          "offset": {
            "type": "number",
            "description": "Number of results skipped",
            "example": 0
          }
        },
        "required": [
          "items",
          "total",
          "limit",
          "offset"
        ]
      },
      "OrganizationStatusCountsDto": {
        "type": "object",
        "properties": {
          "active": {
            "type": "number",
            "description": "Number of active organizations",
            "example": 10
          },
          "suspended": {
            "type": "number",
            "description": "Number of suspended organizations",
            "example": 2
          },
          "pending": {
            "type": "number",
            "description": "Number of pending organizations",
            "example": 3
          },
          "disabled": {
            "type": "number",
            "description": "Number of disabled organizations",
            "example": 1
          }
        },
        "required": [
          "active",
          "suspended",
          "pending",
          "disabled"
        ]
      },
      "OrganizationAggregateStatsDto": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Total number of organizations",
            "example": 16
          },
          "byStatus": {
            "description": "Organization counts grouped by status",
            "allOf": [
              {
                "$ref": "#/components/schemas/OrganizationStatusCountsDto"
              }
            ]
          }
        },
        "required": [
          "total",
          "byStatus"
        ]
      },
      "BulkDeleteOrganizationsResultDto": {
        "type": "object",
        "properties": {
          "deleted": {
            "type": "number",
            "description": "Number of organizations successfully purged"
          },
          "errors": {
            "description": "Per-organization error messages for any that could not be purged (e.g. the active tenant, which is always skipped)",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "devices": {
            "type": "number",
            "description": "Total devices soft-deleted across all purged organizations"
          },
          "deployments": {
            "type": "number",
            "description": "Total deployments soft-deleted (cancelled) across all purged organizations"
          },
          "applications": {
            "type": "number",
            "description": "Total application packages soft-deleted across all purged organizations"
          }
        },
        "required": [
          "deleted",
          "errors",
          "devices",
          "deployments",
          "applications"
        ]
      },
      "OrganizationWithStatsDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique organization identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "name": {
            "type": "string",
            "description": "Organization display name",
            "example": "Acme Corporation"
          },
          "slug": {
            "type": "string",
            "description": "Unique URL-friendly identifier",
            "example": "acme-corp"
          },
          "description": {
            "type": "string",
            "description": "Organization description",
            "example": "Leading manufacturer of industrial automation equipment"
          },
          "settings": {
            "type": "object",
            "description": "Organization-specific settings",
            "additionalProperties": true,
            "example": {
              "timezone": "America/New_York",
              "locale": "en-US"
            }
          },
          "branding": {
            "type": "object",
            "description": "Organization branding configuration",
            "additionalProperties": true,
            "example": {
              "primaryColor": "#003366",
              "logo": "https://example.com/logo.png"
            }
          },
          "status": {
            "type": "string",
            "description": "Organization status",
            "enum": [
              "active",
              "suspended",
              "pending",
              "disabled"
            ],
            "example": "active"
          },
          "lastActivityAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last activity timestamp",
            "example": "2024-06-20T14:45:00Z",
            "nullable": true
          },
          "primaryContact": {
            "type": "string",
            "description": "Primary contact name",
            "example": "John Smith",
            "nullable": true
          },
          "billingEmail": {
            "type": "string",
            "description": "Billing email address",
            "format": "email",
            "example": "billing@acme-corp.com",
            "nullable": true
          },
          "active": {
            "type": "boolean",
            "description": "Whether the organization is active (deprecated, use status)",
            "example": true,
            "deprecated": true
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the organization was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the organization was last updated",
            "example": "2024-06-20T14:45:00Z"
          },
          "stats": {
            "description": "Organization resource statistics",
            "allOf": [
              {
                "$ref": "#/components/schemas/OrganizationStatsDto"
              }
            ]
          },
          "userCount": {
            "type": "number",
            "description": "Total number of users in the organization",
            "example": 25
          },
          "activeUsers": {
            "type": "number",
            "description": "Number of active users in the organization",
            "example": 20
          },
          "deviceCount": {
            "type": "number",
            "description": "Total number of devices in the organization",
            "example": 150
          },
          "activeDevices": {
            "type": "number",
            "description": "Number of online or provisioned devices",
            "example": 120
          },
          "deploymentCount": {
            "type": "number",
            "description": "Total number of deployments in the organization",
            "example": 42
          },
          "runningDeployments": {
            "type": "number",
            "description": "Number of running deployments",
            "example": 35
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "settings",
          "branding",
          "status",
          "active",
          "createdAt",
          "updatedAt",
          "userCount",
          "activeUsers",
          "deviceCount",
          "activeDevices",
          "deploymentCount",
          "runningDeployments"
        ]
      },
      "UpdateOrganizationDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Organization display name",
            "minLength": 2,
            "maxLength": 100,
            "example": "Acme Corporation Inc."
          },
          "description": {
            "type": "string",
            "description": "Organization description",
            "maxLength": 500,
            "example": "Updated description for the organization"
          },
          "settings": {
            "type": "object",
            "description": "Organization-specific settings",
            "additionalProperties": true,
            "example": {
              "timezone": "Europe/London",
              "locale": "en-GB"
            }
          },
          "branding": {
            "type": "object",
            "description": "Organization branding configuration",
            "additionalProperties": true,
            "example": {
              "primaryColor": "#006633",
              "logo": "https://example.com/new-logo.png"
            }
          },
          "active": {
            "type": "boolean",
            "description": "Whether the organization is active (deprecated, use dedicated endpoints)",
            "example": true,
            "deprecated": true
          },
          "primaryContact": {
            "type": "string",
            "description": "Primary contact name for the organization",
            "maxLength": 200,
            "example": "John Smith"
          },
          "billingEmail": {
            "type": "string",
            "description": "Billing email address for the organization",
            "format": "email",
            "example": "billing@acme-corp.com"
          }
        }
      },
      "ForceDeleteResultDto": {
        "type": "object",
        "properties": {
          "removedMemberships": {
            "type": "number",
            "description": "Number of OrganizationUser memberships removed from this tenant"
          },
          "deletedUsers": {
            "type": "number",
            "description": "Number of users hard-deleted because the deleted tenant was their only membership"
          },
          "switchedUsers": {
            "type": "number",
            "description": "Number of users that kept their account because they belong to at least one other tenant"
          },
          "deletedDevices": {
            "type": "number",
            "description": "Number of devices soft-deleted as part of the cascade"
          },
          "cancelledDeployments": {
            "type": "number",
            "description": "Number of deployments soft-deleted (cancelled) as part of the cascade"
          },
          "deletedApplications": {
            "type": "number",
            "description": "Number of application packages soft-deleted as part of the cascade"
          }
        },
        "required": [
          "removedMemberships",
          "deletedUsers",
          "switchedUsers",
          "deletedDevices",
          "cancelledDeployments",
          "deletedApplications"
        ]
      },
      "EnableOrganizationDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for enabling the organization"
          }
        }
      },
      "DisableOrganizationDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for disabling the organization"
          }
        }
      },
      "SuspendOrganizationDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for suspending the organization",
            "maxLength": 500,
            "example": "Payment overdue - account suspended pending resolution"
          }
        }
      },
      "RenameOrganizationDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "New name for the organization"
          },
          "slug": {
            "type": "string",
            "description": "New slug for the organization (optional, auto-generated from name if not provided)"
          }
        },
        "required": [
          "name"
        ]
      },
      "DeletionBlocker": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "Type of blocker",
            "enum": [
              "users",
              "devices",
              "deployments",
              "applications"
            ]
          },
          "count": {
            "type": "number",
            "description": "Count of resources blocking deletion"
          },
          "message": {
            "type": "string",
            "description": "Human-readable message"
          }
        },
        "required": [
          "type",
          "count",
          "message"
        ]
      },
      "DeletionCheckResponseDto": {
        "type": "object",
        "properties": {
          "canDelete": {
            "type": "boolean",
            "description": "Whether the organization can be deleted"
          },
          "blockers": {
            "description": "List of blockers preventing deletion",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeletionBlocker"
            }
          }
        },
        "required": [
          "canDelete",
          "blockers"
        ]
      },
      "OrganizationBulkOperationDto": {
        "type": "object",
        "properties": {
          "userIds": {
            "description": "User IDs to apply the action to",
            "example": [
              "uuid1",
              "uuid2"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "action": {
            "type": "string",
            "description": "Action to perform",
            "enum": [
              "enable_users",
              "disable_users",
              "remove_users"
            ]
          },
          "reason": {
            "type": "string",
            "description": "Reason for the operation"
          }
        },
        "required": [
          "userIds",
          "action"
        ]
      },
      "OrganizationBulkOperationResultDto": {
        "type": "object",
        "properties": {
          "processed": {
            "type": "number",
            "description": "Total number of users processed"
          },
          "successful": {
            "type": "number",
            "description": "Number of successful operations"
          },
          "failed": {
            "type": "number",
            "description": "Number of failed operations"
          },
          "errors": {
            "type": "object",
            "description": "Failed user IDs with error messages",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "required": [
          "processed",
          "successful",
          "failed"
        ]
      },
      "ResetPasswordDto": {
        "type": "object",
        "properties": {
          "delivery": {
            "type": "string",
            "description": "How the reset reaches the user (FM-862). 'email' (the default) is non-destructive: the existing password keeps working and the user sets a new one from the emailed reset form. 'display' is the no-SMTP / air-gap recovery path only: it rotates the credential and returns the generated value once.",
            "enum": [
              "email",
              "display"
            ],
            "default": "email"
          },
          "password": {
            "type": "string",
            "description": "New password. Only honoured for delivery='display'; ignored for delivery='email', where only the user may set the password. If omitted for 'display', a random password is generated and returned once."
          },
          "sendEmail": {
            "type": "boolean",
            "description": "DEPRECATED (FM-862): ignored. Use `delivery` instead — email delivery is now the default and is non-destructive.",
            "deprecated": true
          },
          "reason": {
            "type": "string",
            "description": "Reason for resetting the password"
          }
        }
      },
      "PasswordResetResponseDto": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the password reset itself succeeded"
          },
          "delivery": {
            "type": "string",
            "description": "The delivery mode that was applied.",
            "enum": [
              "email",
              "display"
            ]
          },
          "temporaryPassword": {
            "type": "string",
            "description": "One-time generated password. Present ONLY for delivery='display' when no explicit password was supplied. It is never stored and cannot be retrieved again — the caller must show it to the admin once. Absent for delivery='email', which never rotates the credential."
          },
          "mustChangePassword": {
            "type": "boolean",
            "description": "User must change password on next login"
          },
          "emailSent": {
            "type": "boolean",
            "description": "Whether a reset email was actually sent. False when the user has no linked Keycloak account, Keycloak sync is disabled, or the send failed (FM-602 S2)."
          },
          "emailError": {
            "type": "string",
            "description": "Why the email was not sent, when emailSent is false (e.g. 'no-keycloak-account', 'keycloak-sync-disabled', 'keycloak-send-failed')."
          }
        },
        "required": [
          "success",
          "delivery",
          "mustChangePassword"
        ]
      },
      "CreateUserDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "User email address",
            "format": "email",
            "example": "john.doe@example.com"
          },
          "name": {
            "type": "string",
            "description": "User display name",
            "minLength": 2,
            "maxLength": 100,
            "example": "John Doe"
          },
          "password": {
            "type": "string",
            "description": "User password (min 8 characters)",
            "minLength": 8,
            "format": "password",
            "example": "securePassword123!"
          },
          "isSuperAdmin": {
            "type": "boolean",
            "description": "Whether the user is a super admin with platform-wide privileges",
            "default": false,
            "example": false
          },
          "organizationId": {
            "type": "string",
            "description": "Organization ID to assign the user to upon creation",
            "format": "uuid"
          },
          "roleId": {
            "type": "string",
            "description": "Role ID to assign to the user in the organization (defaults to viewer)",
            "format": "uuid"
          }
        },
        "required": [
          "email",
          "name"
        ]
      },
      "UserResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique user identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "email": {
            "type": "string",
            "description": "User email address",
            "format": "email",
            "example": "john.doe@example.com"
          },
          "name": {
            "type": "string",
            "description": "User display name",
            "example": "John Doe"
          },
          "active": {
            "type": "boolean",
            "description": "Whether the user account is active",
            "example": true
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Whether the user email has been verified",
            "example": true
          },
          "isSuperAdmin": {
            "type": "boolean",
            "description": "Whether the user is a super admin with platform-wide privileges",
            "example": false
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the user was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the user was last updated",
            "example": "2024-06-20T14:45:00Z"
          },
          "lastLoginAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the user last logged in",
            "example": "2024-12-20T09:15:00Z"
          },
          "identityProvider": {
            "type": "string",
            "description": "FM-1069: the Keycloak identity-provider alias the user last authenticated through (e.g. the Entra broker alias), or null for a local realm login.",
            "example": "entra"
          },
          "isFederated": {
            "type": "boolean",
            "description": "FM-1069: whether the user authenticates through an external identity provider (federated). The UI hides the password-reset action when true, since federated users have no local password to reset.",
            "example": false
          },
          "pendingEmailVerification": {
            "type": "boolean",
            "description": "True when this account cannot sign in to its organizations because its e-mail address is unverified AND it is not yet bound to a Keycloak identity. Both conditions matter: an unverified address only blocks the e-mail-keyed binding (FM-837), which is never attempted for an account that is already bound. An already-bound account with an unverified address signs in perfectly well, so flagging it would be a false alarm.",
            "example": false
          },
          "hasKeycloakLink": {
            "type": "boolean",
            "description": "FM-1286: whether this account is bound to a Keycloak identity (users.keycloakId is set). A Super Admin can clear the binding when the Keycloak account was re-created, so the account re-links on its next sign-in with memberships intact. The Keycloak id itself is not exposed.",
            "example": true
          }
        },
        "required": [
          "id",
          "email",
          "active",
          "emailVerified",
          "isSuperAdmin",
          "createdAt",
          "updatedAt",
          "isFederated",
          "pendingEmailVerification",
          "hasKeycloakLink"
        ]
      },
      "UserListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of users",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of users matching the query",
            "example": 100
          },
          "limit": {
            "type": "number",
            "description": "Number of results returned",
            "example": 50
          },
          "offset": {
            "type": "number",
            "description": "Number of results skipped",
            "example": 0
          }
        },
        "required": [
          "items",
          "total",
          "limit",
          "offset"
        ]
      },
      "UserStatusCountsDto": {
        "type": "object",
        "properties": {
          "active": {
            "type": "number",
            "description": "Number of active users",
            "example": 20
          },
          "inactive": {
            "type": "number",
            "description": "Number of inactive/disabled users",
            "example": 3
          }
        },
        "required": [
          "active",
          "inactive"
        ]
      },
      "UserAggregateStatsDto": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Total number of users",
            "example": 25
          },
          "byStatus": {
            "description": "User counts by status",
            "allOf": [
              {
                "$ref": "#/components/schemas/UserStatusCountsDto"
              }
            ]
          },
          "byRole": {
            "type": "object",
            "description": "Organization-membership counts keyed by role name. Roles are organization-scoped, so the keys are whatever roles exist. Org-scoped this equals a user count; platform-wide a user in several organizations is counted once per membership.",
            "additionalProperties": {
              "type": "integer"
            },
            "example": {
              "admin": 3,
              "operator": 8,
              "viewer": 14
            }
          },
          "pendingInvitations": {
            "type": "number",
            "description": "Number of invitations that are neither accepted nor expired. These are not users and are deliberately not part of byStatus.",
            "example": 2
          }
        },
        "required": [
          "total",
          "byStatus",
          "byRole",
          "pendingInvitations"
        ]
      },
      "OrganizationMembershipDto": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "string",
            "description": "Organization UUID",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "organizationName": {
            "type": "string",
            "description": "Organization display name",
            "example": "Acme Corporation"
          },
          "organizationSlug": {
            "type": "string",
            "description": "Organization URL-friendly slug",
            "example": "acme-corp"
          },
          "roleId": {
            "type": "string",
            "description": "Role UUID",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          },
          "roleName": {
            "type": "string",
            "description": "Role display name",
            "example": "Administrator"
          },
          "permissions": {
            "description": "List of permissions granted by this role",
            "example": [
              "devices:read",
              "devices:write",
              "deployments:read"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "organizationId",
          "organizationName",
          "organizationSlug",
          "roleId",
          "roleName",
          "permissions"
        ]
      },
      "UserWithOrganizationsDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique user identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "email": {
            "type": "string",
            "description": "User email address",
            "format": "email",
            "example": "john.doe@example.com"
          },
          "name": {
            "type": "string",
            "description": "User display name",
            "example": "John Doe"
          },
          "active": {
            "type": "boolean",
            "description": "Whether the user account is active",
            "example": true
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Whether the user email has been verified",
            "example": true
          },
          "isSuperAdmin": {
            "type": "boolean",
            "description": "Whether the user is a super admin with platform-wide privileges",
            "example": false
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the user was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the user was last updated",
            "example": "2024-06-20T14:45:00Z"
          },
          "lastLoginAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the user last logged in",
            "example": "2024-12-20T09:15:00Z"
          },
          "identityProvider": {
            "type": "string",
            "description": "FM-1069: the Keycloak identity-provider alias the user last authenticated through (e.g. the Entra broker alias), or null for a local realm login.",
            "example": "entra"
          },
          "isFederated": {
            "type": "boolean",
            "description": "FM-1069: whether the user authenticates through an external identity provider (federated). The UI hides the password-reset action when true, since federated users have no local password to reset.",
            "example": false
          },
          "pendingEmailVerification": {
            "type": "boolean",
            "description": "True when this account cannot sign in to its organizations because its e-mail address is unverified AND it is not yet bound to a Keycloak identity. Both conditions matter: an unverified address only blocks the e-mail-keyed binding (FM-837), which is never attempted for an account that is already bound. An already-bound account with an unverified address signs in perfectly well, so flagging it would be a false alarm.",
            "example": false
          },
          "hasKeycloakLink": {
            "type": "boolean",
            "description": "FM-1286: whether this account is bound to a Keycloak identity (users.keycloakId is set). A Super Admin can clear the binding when the Keycloak account was re-created, so the account re-links on its next sign-in with memberships intact. The Keycloak id itself is not exposed.",
            "example": true
          },
          "organizations": {
            "description": "List of organizations the user belongs to",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganizationMembershipDto"
            }
          }
        },
        "required": [
          "id",
          "email",
          "active",
          "emailVerified",
          "isSuperAdmin",
          "createdAt",
          "updatedAt",
          "isFederated",
          "pendingEmailVerification",
          "hasKeycloakLink",
          "organizations"
        ]
      },
      "UpdateUserDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "User display name",
            "minLength": 2,
            "maxLength": 100,
            "example": "John Smith",
            "nullable": true
          },
          "active": {
            "type": "boolean",
            "description": "Whether the user account is active",
            "example": true
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Whether the user email has been verified",
            "example": true
          }
        }
      },
      "AddUserToOrganizationDto": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "string",
            "description": "Organization UUID to add the user to",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "roleId": {
            "type": "string",
            "description": "Role UUID to assign to the user in the organization",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          }
        },
        "required": [
          "organizationId",
          "roleId"
        ]
      },
      "ResendVerificationResponseDto": {
        "type": "object",
        "properties": {
          "sent": {
            "type": "boolean",
            "description": "Whether Keycloak accepted the request to send the message. False means the user is still flagged to verify at next sign-in, but no e-mail was delivered.",
            "example": true
          },
          "email": {
            "type": "string",
            "description": "The address the message was sent to.",
            "example": "user@example.com"
          },
          "error": {
            "type": "string",
            "description": "Why the send failed, when it did. Absent on success.",
            "example": "SMTP server not configured for this realm"
          }
        },
        "required": [
          "sent",
          "email"
        ]
      },
      "UpdateUserRoleDto": {
        "type": "object",
        "properties": {
          "roleId": {
            "type": "string",
            "description": "New role UUID to assign to the user",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          }
        },
        "required": [
          "roleId"
        ]
      },
      "EnableUserDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for enabling the user"
          }
        }
      },
      "DisableUserDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for disabling the user"
          }
        }
      },
      "BulkOperationDto": {
        "type": "object",
        "properties": {
          "userIds": {
            "description": "User IDs to apply the action to",
            "example": [
              "uuid1",
              "uuid2"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "action": {
            "type": "string",
            "description": "Action to perform",
            "enum": [
              "enable",
              "disable",
              "delete",
              "reset_password",
              "assign_role"
            ]
          },
          "roleId": {
            "type": "string",
            "description": "Role ID for assign_role action"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization ID for assign_role action"
          },
          "reason": {
            "type": "string",
            "description": "Reason for the operation"
          }
        },
        "required": [
          "userIds",
          "action"
        ]
      },
      "BulkUserOutcomeDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "Target user ID"
          },
          "success": {
            "type": "boolean",
            "description": "Whether the operation fully succeeded for this user"
          },
          "emailSent": {
            "type": "boolean",
            "description": "For reset_password: whether the reset email was actually delivered. False means the target was NOT counted as successful."
          },
          "error": {
            "type": "string",
            "description": "Failure reason when success is false"
          }
        },
        "required": [
          "userId",
          "success"
        ]
      },
      "BulkOperationResultDto": {
        "type": "object",
        "properties": {
          "processed": {
            "type": "number",
            "description": "Total number of users processed"
          },
          "successful": {
            "type": "number",
            "description": "Number of successful operations"
          },
          "failed": {
            "type": "number",
            "description": "Number of failed operations"
          },
          "errors": {
            "type": "object",
            "description": "Failed user IDs with error messages",
            "additionalProperties": {
              "type": "string"
            }
          },
          "outcomes": {
            "description": "Per-user outcome (FM-862). Populated for reset_password, where an undelivered reset is NOT counted as successful and carries emailSent=false.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BulkUserOutcomeDto"
            }
          }
        },
        "required": [
          "processed",
          "successful",
          "failed"
        ]
      },
      "ExportUsersDto": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "description": "Export format",
            "enum": [
              "csv",
              "json"
            ],
            "default": "csv"
          },
          "organizationId": {
            "type": "string",
            "description": "Filter by organization ID"
          },
          "active": {
            "type": "boolean",
            "description": "Filter by active status"
          },
          "userIds": {
            "description": "Specific user IDs to export",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "UserProfileDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "User ID"
          },
          "email": {
            "type": "string",
            "description": "User email"
          },
          "name": {
            "type": "string",
            "description": "User display name"
          },
          "phone": {
            "type": "string",
            "description": "Phone number"
          },
          "timezone": {
            "type": "string",
            "description": "Timezone",
            "default": "UTC"
          },
          "locale": {
            "type": "string",
            "description": "Locale",
            "default": "en"
          },
          "emailNotifications": {
            "type": "boolean",
            "description": "Email notifications enabled",
            "default": true
          },
          "smsNotifications": {
            "type": "boolean",
            "description": "SMS notifications enabled",
            "default": false
          },
          "pushNotifications": {
            "type": "boolean",
            "description": "Push notifications enabled",
            "default": true
          },
          "marketingEmails": {
            "type": "boolean",
            "description": "Marketing emails enabled",
            "default": false
          },
          "bio": {
            "type": "string",
            "description": "User bio"
          },
          "jobTitle": {
            "type": "string",
            "description": "Job title"
          },
          "department": {
            "type": "string",
            "description": "Department"
          },
          "location": {
            "type": "string",
            "description": "Location"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Created at"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Updated at"
          },
          "lastLoginAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last login timestamp"
          },
          "isSuperAdmin": {
            "type": "boolean",
            "description": "Whether the user is a super admin",
            "default": false
          },
          "roles": {
            "description": "Effective roles for the current user. A SUPPORT user with an open tenant switch reports ['support', 'admin']: it works in the switched tenant with the permission set of the built-in admin role (FM-1331).",
            "example": [
              "admin"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isEntraFederationEnabled": {
            "type": "boolean",
            "description": "Whether the realm has Entra federation enabled (FM-1087). When true, manual role changes, invites, password resets, user creation and superadmin grant/revoke are governed by Entra mappings instead.",
            "default": false
          },
          "isFederated": {
            "type": "boolean",
            "description": "Whether the CURRENT session user is federated (has a linked external identity provider, e.g. Entra). Derived from the users.identityProvider column (FM-1087). Used together with isEntraFederationEnabled to warn a non-federated user signed in on an Entra-enabled realm.",
            "default": false
          }
        },
        "required": [
          "id",
          "email",
          "timezone",
          "locale",
          "emailNotifications",
          "smsNotifications",
          "pushNotifications",
          "marketingEmails",
          "createdAt",
          "updatedAt",
          "isSuperAdmin",
          "roles",
          "isEntraFederationEnabled",
          "isFederated"
        ]
      },
      "UpdateProfileDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Display name"
          },
          "phone": {
            "type": "string",
            "description": "Phone number"
          },
          "timezone": {
            "type": "string",
            "description": "Timezone"
          },
          "locale": {
            "type": "string",
            "description": "Locale"
          },
          "bio": {
            "type": "string",
            "description": "Bio"
          },
          "jobTitle": {
            "type": "string",
            "description": "Job title"
          },
          "department": {
            "type": "string",
            "description": "Department"
          },
          "location": {
            "type": "string",
            "description": "Location"
          }
        }
      },
      "UpdateNotificationPreferencesDto": {
        "type": "object",
        "properties": {
          "emailNotifications": {
            "type": "boolean",
            "description": "Email notifications enabled"
          },
          "smsNotifications": {
            "type": "boolean",
            "description": "SMS notifications enabled"
          },
          "pushNotifications": {
            "type": "boolean",
            "description": "Push notifications enabled"
          },
          "marketingEmails": {
            "type": "boolean",
            "description": "Marketing emails enabled"
          }
        }
      },
      "ChangePasswordDto": {
        "type": "object",
        "properties": {
          "currentPassword": {
            "type": "string",
            "description": "Current password"
          },
          "newPassword": {
            "type": "string",
            "description": "New password"
          }
        },
        "required": [
          "currentPassword",
          "newPassword"
        ]
      },
      "UserSessionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Session ID"
          },
          "ipAddress": {
            "type": "string",
            "description": "IP address"
          },
          "userAgent": {
            "type": "string",
            "description": "User agent"
          },
          "deviceType": {
            "type": "string",
            "description": "Device type"
          },
          "browser": {
            "type": "string",
            "description": "Browser"
          },
          "operatingSystem": {
            "type": "string",
            "description": "Operating system"
          },
          "location": {
            "type": "string",
            "description": "Location"
          },
          "startedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Session started at"
          },
          "lastActivityAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last activity at"
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "description": "Session expires at"
          },
          "isCurrent": {
            "type": "boolean",
            "description": "Is current session"
          },
          "isActive": {
            "type": "boolean",
            "description": "Is active (not terminated or expired)"
          }
        },
        "required": [
          "id",
          "ipAddress",
          "startedAt",
          "lastActivityAt",
          "expiresAt",
          "isCurrent",
          "isActive"
        ]
      },
      "SessionListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Sessions",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserSessionDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total count"
          }
        },
        "required": [
          "items",
          "total"
        ]
      },
      "UserActivityDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Activity ID"
          },
          "activityType": {
            "type": "string",
            "description": "Activity type"
          },
          "ipAddress": {
            "type": "string",
            "description": "IP address"
          },
          "description": {
            "type": "string",
            "description": "Description"
          },
          "success": {
            "type": "boolean",
            "description": "Success status"
          },
          "errorMessage": {
            "type": "string",
            "description": "Error message"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Created at"
          },
          "metadata": {
            "type": "object",
            "description": "Metadata"
          }
        },
        "required": [
          "id",
          "activityType",
          "success",
          "createdAt"
        ]
      },
      "ActivityListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Activities",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserActivityDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total count"
          },
          "limit": {
            "type": "number",
            "description": "Limit"
          },
          "offset": {
            "type": "number",
            "description": "Offset"
          }
        },
        "required": [
          "items",
          "total",
          "limit",
          "offset"
        ]
      },
      "TenantConsistencyOrganizationRefDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Organization name, or null when the id names no organization (unknown)."
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "TenantConsistencyEmailVerifiedDto": {
        "type": "object",
        "properties": {
          "database": {
            "type": "boolean",
            "nullable": true,
            "description": "users.emailVerified in the logiccloud orchestrate database, or null for a \"keycloak-only\" account that has no row (FM-1441)."
          },
          "keycloak": {
            "type": "boolean",
            "nullable": true,
            "description": "emailVerified in Keycloak, or null when there is no Keycloak account to read."
          }
        },
        "required": [
          "database",
          "keycloak"
        ]
      },
      "TenantConsistencyRowDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "logiccloud orchestrate user id; null for a \"keycloak-only\" account (FM-1441)."
          },
          "email": {
            "type": "string"
          },
          "keycloakId": {
            "type": "string",
            "nullable": true,
            "description": "Keycloak user id, if linked."
          },
          "classification": {
            "type": "string",
            "enum": [
              "consistent",
              "hanging",
              "unassigned",
              "keycloak-attribute-names-unknown-org",
              "keycloak-only"
            ]
          },
          "databasePrimaryOrganization": {
            "nullable": true,
            "description": "Database primary organization, or null when the user has no membership.",
            "type": "object",
            "allOf": [
              {
                "$ref": "#/components/schemas/TenantConsistencyOrganizationRefDto"
              }
            ]
          },
          "keycloakOrganizations": {
            "description": "Organizations named by the Keycloak organization_id attribute, de-duplicated (name is null when an id is unknown). A set: Keycloak does not preserve attribute order, so no position carries meaning.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TenantConsistencyOrganizationRefDto"
            }
          },
          "keycloakOrganizationIds": {
            "description": "Every value of the Keycloak organization_id attribute, de-duplicated. Order is not meaningful.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "missingInKeycloak": {
            "description": "Organizations the user is a member of that the Keycloak attribute does not contain (the whole membership set when the attribute or the Keycloak account is absent).",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TenantConsistencyOrganizationRefDto"
            }
          },
          "extraInKeycloak": {
            "description": "Organizations named by the Keycloak attribute that the user is not a member of, including ids of organizations that do not exist (name is null).",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TenantConsistencyOrganizationRefDto"
            }
          },
          "keycloakLinked": {
            "type": "boolean",
            "description": "Whether the logiccloud orchestrate user row is linked to a Keycloak account."
          },
          "emailVerified": {
            "$ref": "#/components/schemas/TenantConsistencyEmailVerifiedDto"
          }
        },
        "required": [
          "userId",
          "email",
          "keycloakId",
          "classification",
          "databasePrimaryOrganization",
          "keycloakOrganizations",
          "keycloakOrganizationIds",
          "missingInKeycloak",
          "extraInKeycloak",
          "keycloakLinked",
          "emailVerified"
        ]
      },
      "TenantConsistencySummaryDto": {
        "type": "object",
        "properties": {
          "consistent": {
            "type": "number"
          },
          "hanging": {
            "type": "number"
          },
          "unassigned": {
            "type": "number"
          },
          "keycloakAttributeNamesUnknownOrg": {
            "type": "number",
            "description": "Count of \"keycloak-attribute-names-unknown-org\" users."
          },
          "keycloakUsersWithoutFleetManagerRow": {
            "type": "number",
            "description": "Keycloak accounts with no logiccloud orchestrate user row, listed with the classification \"keycloak-only\" (FM-1441). Service-account users are not counted."
          }
        },
        "required": [
          "consistent",
          "hanging",
          "unassigned",
          "keycloakAttributeNamesUnknownOrg",
          "keycloakUsersWithoutFleetManagerRow"
        ]
      },
      "TenantConsistencyListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TenantConsistencyRowDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Rows matching the filter, before pagination."
          },
          "page": {
            "type": "number"
          },
          "limit": {
            "type": "number"
          },
          "summary": {
            "description": "Counts over ALL users.",
            "allOf": [
              {
                "$ref": "#/components/schemas/TenantConsistencySummaryDto"
              }
            ]
          }
        },
        "required": [
          "data",
          "total",
          "page",
          "limit",
          "summary"
        ]
      },
      "AnchorTenantDto": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "string",
            "format": "uuid",
            "description": "Organization to make the user primary in, in both logiccloud orchestrate and Keycloak."
          },
          "roleId": {
            "type": "string",
            "format": "uuid",
            "description": "Role for a NEW membership; defaults to the system viewer role when the user is not yet a member. Rejected (400) when the user already is a member, because the existing role is kept."
          }
        },
        "required": [
          "organizationId"
        ]
      },
      "AnchorTenantResponseDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string"
          },
          "organizationId": {
            "type": "string",
            "format": "uuid"
          },
          "membershipCreated": {
            "type": "boolean",
            "description": "True when the user was not a member and a membership was created."
          },
          "previousPrimaryOrganizationId": {
            "type": "string",
            "nullable": true,
            "format": "uuid"
          },
          "previousKeycloakOrganizationIds": {
            "description": "organization_id attribute before the anchor, as Keycloak reported it.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "keycloakOrganizationIds": {
            "description": "organization_id attribute now: exactly the membership set, de-duplicated. Order is not meaningful.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "keycloakUpdated": {
            "type": "boolean",
            "description": "False when the user has no live Keycloak account (never linked, or the linked account no longer exists): only the database primary was set (FM-1441)."
          },
          "keycloakLinkCleared": {
            "type": "boolean",
            "description": "True when the linked Keycloak account no longer exists and the stale link was removed; the user is linked again on the next sign-in (FM-1441)."
          },
          "userCreated": {
            "type": "boolean",
            "description": "True when a logiccloud orchestrate user row was created for a Keycloak-only account (FM-1441)."
          }
        },
        "required": [
          "userId",
          "email",
          "organizationId",
          "membershipCreated",
          "previousPrimaryOrganizationId",
          "previousKeycloakOrganizationIds",
          "keycloakOrganizationIds",
          "keycloakUpdated",
          "keycloakLinkCleared",
          "userCreated"
        ]
      },
      "CreateApiKeyDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Display name for the API key",
            "example": "Production CI/CD Key",
            "minLength": 2,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "description": "Description of the API key purpose",
            "example": "Used by GitHub Actions for automated deployments",
            "maxLength": 500
          },
          "permissions": {
            "description": "List of permission keys to grant to this API key",
            "example": [
              "devices:read",
              "deployments:read",
              "deployments:write"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expiresAt": {
            "type": "string",
            "description": "Expiration date for the API key in ISO 8601 format",
            "format": "date-time",
            "example": "2025-12-31T23:59:59Z"
          }
        },
        "required": [
          "name"
        ]
      },
      "ApiKeyCreatedResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique API key identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "name": {
            "type": "string",
            "description": "Display name for the API key",
            "example": "Production CI/CD Key"
          },
          "description": {
            "type": "string",
            "description": "Description of the API key purpose",
            "example": "Used by GitHub Actions for automated deployments"
          },
          "keyPrefix": {
            "type": "string",
            "description": "Prefix of the API key for identification (first 8 characters)",
            "example": "mwfm_abc"
          },
          "permissions": {
            "description": "List of permission keys granted to this API key",
            "example": [
              "devices:read",
              "deployments:read",
              "deployments:write"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "lastUsedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the API key was last used",
            "example": "2024-06-20T14:45:00Z"
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the API key expires",
            "example": "2025-12-31T23:59:59Z"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the API key was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "createdBy": {
            "type": "string",
            "description": "User ID who created this API key",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "key": {
            "type": "string",
            "description": "Full API key value. Only returned on creation - store securely as it cannot be retrieved again.",
            "example": "mwfm_abc123def456ghi789jkl012mno345pqr"
          }
        },
        "required": [
          "id",
          "name",
          "keyPrefix",
          "permissions",
          "createdAt",
          "key"
        ]
      },
      "ApiKeyResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique API key identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "name": {
            "type": "string",
            "description": "Display name for the API key",
            "example": "Production CI/CD Key"
          },
          "description": {
            "type": "string",
            "description": "Description of the API key purpose",
            "example": "Used by GitHub Actions for automated deployments"
          },
          "keyPrefix": {
            "type": "string",
            "description": "Prefix of the API key for identification (first 8 characters)",
            "example": "mwfm_abc"
          },
          "permissions": {
            "description": "List of permission keys granted to this API key",
            "example": [
              "devices:read",
              "deployments:read",
              "deployments:write"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "lastUsedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the API key was last used",
            "example": "2024-06-20T14:45:00Z"
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the API key expires",
            "example": "2025-12-31T23:59:59Z"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the API key was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "createdBy": {
            "type": "string",
            "description": "User ID who created this API key",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          }
        },
        "required": [
          "id",
          "name",
          "keyPrefix",
          "permissions",
          "createdAt"
        ]
      },
      "ApiKeyListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of API keys",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKeyResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of API keys matching the query",
            "example": 5
          },
          "limit": {
            "type": "number",
            "description": "Number of results returned",
            "example": 20
          },
          "offset": {
            "type": "number",
            "description": "Number of results skipped",
            "example": 0
          }
        },
        "required": [
          "items",
          "total",
          "limit",
          "offset"
        ]
      },
      "UpdateApiKeyDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Updated display name for the API key",
            "example": "Production CI/CD Key v2",
            "minLength": 2,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "description": "Updated description of the API key purpose",
            "example": "Used by GitHub Actions and Jenkins for deployments",
            "maxLength": 500
          },
          "permissions": {
            "description": "Updated list of permission keys for this API key",
            "example": [
              "devices:read",
              "devices:write",
              "deployments:read"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CreateInvitationDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "Email address to send the invitation to",
            "format": "email",
            "example": "newuser@example.com"
          },
          "roleId": {
            "type": "string",
            "description": "Role ID to assign to the user when they accept the invitation",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "expiresInDays": {
            "type": "number",
            "description": "Number of days until the invitation expires",
            "minimum": 1,
            "maximum": 30,
            "default": 7,
            "example": 7
          },
          "department": {
            "type": "string",
            "description": "Department to assign to the user when they accept the invitation",
            "example": "Engineering",
            "maxLength": 200
          },
          "personalMessage": {
            "type": "string",
            "description": "Personal message to include in the invitation email",
            "example": "Welcome to the team! Looking forward to working with you.",
            "maxLength": 1000
          }
        },
        "required": [
          "email",
          "roleId"
        ]
      },
      "InvitationResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique invitation identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "email": {
            "type": "string",
            "description": "Email address the invitation was sent to",
            "format": "email",
            "example": "newuser@example.com"
          },
          "roleId": {
            "type": "string",
            "description": "Role ID that will be assigned when the invitation is accepted",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization ID the user will be added to",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          },
          "organizationName": {
            "type": "string",
            "description": "Name of the organization",
            "example": "Acme Corporation"
          },
          "invitedById": {
            "type": "string",
            "description": "User ID who sent the invitation (null if inviter was deleted)",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440003"
          },
          "invitedByEmail": {
            "type": "string",
            "description": "Email of the user who sent the invitation (null if inviter was deleted)",
            "format": "email",
            "example": "admin@acme.com"
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the invitation expires",
            "example": "2024-06-27T10:30:00Z"
          },
          "acceptedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the invitation was accepted (null if pending)",
            "example": "2024-06-21T14:45:00Z"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the invitation was created",
            "example": "2024-06-20T10:30:00Z"
          },
          "isPending": {
            "type": "boolean",
            "description": "Whether the invitation is pending (not accepted)",
            "example": true
          },
          "isExpired": {
            "type": "boolean",
            "description": "Whether the invitation has expired",
            "example": false
          }
        },
        "required": [
          "id",
          "email",
          "roleId",
          "organizationId",
          "organizationName",
          "expiresAt",
          "createdAt",
          "isPending",
          "isExpired"
        ]
      },
      "InvitationListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of invitations",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvitationResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of invitations matching the query",
            "example": 5
          }
        },
        "required": [
          "items",
          "total"
        ]
      },
      "ResendInvitationDto": {
        "type": "object",
        "properties": {
          "expiresInDays": {
            "type": "number",
            "description": "Number of days until the invitation expires (resets the expiration)",
            "minimum": 1,
            "maximum": 30,
            "default": 7,
            "example": 7
          }
        }
      },
      "InvitationDetailsDto": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Invitation token",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "email": {
            "type": "string",
            "description": "Email address the invitation was sent to",
            "format": "email",
            "example": "newuser@example.com"
          },
          "organizationName": {
            "type": "string",
            "description": "Name of the organization being invited to",
            "example": "Acme Corporation"
          },
          "organizationSlug": {
            "type": "string",
            "description": "URL-friendly slug of the organization",
            "example": "acme-corporation"
          },
          "invitedByName": {
            "type": "string",
            "description": "Name of the user who sent the invitation",
            "example": "Admin User"
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the invitation expires",
            "example": "2024-06-27T10:30:00Z"
          },
          "userExists": {
            "type": "boolean",
            "description": "True when a user with this email already exists. The accept form should skip the password field in that case — credentials are preserved and the existing user is just added to the new tenant.",
            "example": false
          }
        },
        "required": [
          "token",
          "email",
          "organizationName",
          "organizationSlug",
          "expiresAt",
          "userExists"
        ]
      },
      "AcceptInvitationDto": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Invitation token from the invitation email",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "name": {
            "type": "string",
            "description": "Display name for the new user (required if user does not exist)",
            "example": "John Doe",
            "minLength": 2,
            "maxLength": 100
          },
          "password": {
            "type": "string",
            "description": "Password for the new user (required if user does not exist)",
            "example": "SecureP@ssw0rd!",
            "minLength": 8,
            "maxLength": 128
          }
        },
        "required": [
          "token"
        ]
      },
      "PermissionDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Unique permission key",
            "example": "devices:write"
          },
          "name": {
            "type": "string",
            "description": "Human-readable permission name",
            "example": "Write Devices"
          },
          "description": {
            "type": "string",
            "description": "Permission description",
            "example": "Allows creating, updating, and deleting devices"
          },
          "category": {
            "type": "string",
            "description": "Permission category for grouping",
            "example": "Devices"
          }
        },
        "required": [
          "key",
          "name",
          "description",
          "category"
        ]
      },
      "PermissionListResponseDto": {
        "type": "object",
        "properties": {
          "permissions": {
            "description": "List of available permissions",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PermissionDto"
            }
          }
        },
        "required": [
          "permissions"
        ]
      },
      "RoleResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique role identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "name": {
            "type": "string",
            "description": "Role display name",
            "example": "Device Operator"
          },
          "description": {
            "type": "string",
            "description": "Role description",
            "example": "Can view and manage devices but not deployments"
          },
          "permissions": {
            "description": "List of permission keys assigned to this role",
            "example": [
              "devices:read",
              "devices:write",
              "deployments:read"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isSystem": {
            "type": "boolean",
            "description": "Whether this is a system-defined role (cannot be modified)",
            "example": false
          },
          "organizationId": {
            "type": "string",
            "description": "Organization ID (null for system roles)",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the role was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the role was last updated",
            "example": "2024-06-20T14:45:00Z"
          }
        },
        "required": [
          "id",
          "name",
          "permissions",
          "isSystem",
          "createdAt",
          "updatedAt"
        ]
      },
      "RoleListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of roles",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RoleResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of roles matching the query",
            "example": 10
          },
          "limit": {
            "type": "number",
            "description": "Number of results returned",
            "example": 10
          },
          "offset": {
            "type": "number",
            "description": "Number of results skipped",
            "example": 0
          }
        },
        "required": [
          "items",
          "total",
          "limit",
          "offset"
        ]
      },
      "CreateRoleDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Role display name",
            "minLength": 2,
            "maxLength": 50,
            "example": "Device Operator"
          },
          "description": {
            "type": "string",
            "description": "Role description",
            "maxLength": 200,
            "example": "Can view and manage devices but not deployments"
          },
          "permissions": {
            "description": "List of permission keys assigned to this role",
            "example": [
              "devices:read",
              "devices:write",
              "deployments:read"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "name",
          "permissions"
        ]
      },
      "UpdateRoleDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Role display name",
            "minLength": 2,
            "maxLength": 50,
            "example": "Senior Device Operator"
          },
          "description": {
            "type": "string",
            "description": "Role description",
            "maxLength": 200,
            "example": "Can view and manage devices and deployments"
          },
          "permissions": {
            "description": "List of permission keys assigned to this role",
            "example": [
              "devices:read",
              "devices:write",
              "deployments:read",
              "deployments:write"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "AuditLogResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique audit log identifier",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization ID",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "actorType": {
            "type": "string",
            "enum": [
              "USER",
              "API_KEY",
              "DEVICE",
              "SYSTEM"
            ],
            "description": "Actor type discriminator (FM-922 Phase 3).",
            "example": "USER"
          },
          "userId": {
            "type": "string",
            "description": "User ID who performed the action",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          },
          "userName": {
            "type": "string",
            "description": "Display name of the user at the time of the action. Captured at write time so audit entries remain readable after the user is deleted.",
            "example": "Alice Admin"
          },
          "apiKeyId": {
            "type": "string",
            "description": "API key ID used for the action",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440003"
          },
          "apiKeyName": {
            "type": "string",
            "description": "Display name of the API key at the time of the action.",
            "example": "ci-publisher"
          },
          "deviceId": {
            "type": "string",
            "description": "Internal Device.id (UUID) when actorType is DEVICE.",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440005"
          },
          "deviceName": {
            "type": "string",
            "description": "Device.name captured at write time (survives device deletion).",
            "example": "Edge Gateway 1"
          },
          "deviceClientId": {
            "type": "string",
            "description": "Margo clientId captured at write time.",
            "example": "781d48bf-5ff9-b574-d95e-8d8b557022d2"
          },
          "action": {
            "type": "string",
            "description": "Type of action performed",
            "enum": [
              "CREATE",
              "READ",
              "UPDATE",
              "DELETE",
              "LOGIN",
              "LOGOUT",
              "LOGIN_FAILED",
              "PERMISSION_GRANTED",
              "PERMISSION_DENIED",
              "EXPORT",
              "IMPORT",
              "BULK_UPDATE",
              "BULK_DELETE",
              "PASSWORD_CHANGE",
              "API_KEY_CREATED",
              "API_KEY_REVOKED",
              "CERTIFICATE_ISSUED",
              "CERTIFICATE_REVOKED",
              "DEPLOYMENT_STARTED",
              "DEPLOYMENT_COMPLETED",
              "DEPLOYMENT_FAILED",
              "DEPLOYMENT_ROLLED_BACK",
              "DEPLOYMENT_REDEPLOYED",
              "DEVICE_ONBOARDING_REJECTED",
              "DEVICE_CAPABILITY_REPORT_REJECTED",
              "DEVICE_STATUS_REPORT_REJECTED",
              "DEVICE_DESIRED_STATE_FETCH_REJECTED",
              "DEVICE_ONBOARDING_REJECTED_SUMMARY"
            ],
            "example": "CREATE"
          },
          "entityType": {
            "type": "string",
            "description": "Entity type that was affected",
            "example": "Device"
          },
          "entityId": {
            "type": "string",
            "description": "ID of the affected entity",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440004"
          },
          "entityName": {
            "type": "string",
            "description": "Human-readable name of the affected entity",
            "example": "Production Gateway 1"
          },
          "beforeState": {
            "type": "object",
            "description": "State of the entity before the change",
            "additionalProperties": true,
            "example": {
              "status": "OFFLINE",
              "name": "Old Name"
            }
          },
          "afterState": {
            "type": "object",
            "description": "State of the entity after the change",
            "additionalProperties": true,
            "example": {
              "status": "ONLINE",
              "name": "New Name"
            }
          },
          "changedFields": {
            "description": "List of fields that were changed",
            "example": [
              "status",
              "name"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ipAddress": {
            "type": "string",
            "description": "IP address of the client",
            "example": "192.168.1.100"
          },
          "userAgent": {
            "type": "string",
            "description": "User agent of the client",
            "example": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
          },
          "requestId": {
            "type": "string",
            "description": "Request correlation ID",
            "example": "req-123456"
          },
          "sessionId": {
            "type": "string",
            "description": "Session ID",
            "example": "sess-789012"
          },
          "success": {
            "type": "boolean",
            "description": "Whether the action was successful",
            "example": true
          },
          "errorMessage": {
            "type": "string",
            "description": "Error message if action failed",
            "example": "Permission denied"
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata",
            "additionalProperties": true,
            "example": {
              "source": "api",
              "version": "1.0"
            }
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the audit log was created",
            "example": "2024-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "organizationId",
          "action",
          "entityType",
          "changedFields",
          "success",
          "createdAt"
        ]
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Total number of matching records",
            "example": 150
          },
          "page": {
            "type": "number",
            "description": "Current page number",
            "example": 1
          },
          "limit": {
            "type": "number",
            "description": "Number of records per page",
            "example": 50
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether more records are available",
            "example": true
          },
          "nextCursor": {
            "type": "string",
            "description": "Cursor for next page",
            "example": "eyJpZCI6IjEyMzQifQ=="
          }
        },
        "required": [
          "total",
          "page",
          "limit",
          "hasMore"
        ]
      },
      "AuditLogListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "List of audit log entries",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditLogResponseDto"
            }
          },
          "meta": {
            "description": "Pagination metadata",
            "allOf": [
              {
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "required": [
          "data",
          "meta"
        ]
      },
      "UserAuditCount": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User ID",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "count": {
            "type": "number",
            "description": "Number of audit entries",
            "example": 42
          }
        },
        "required": [
          "userId",
          "count"
        ]
      },
      "AuditStatsResponseDto": {
        "type": "object",
        "properties": {
          "totalCount": {
            "type": "number",
            "description": "Total number of audit entries",
            "example": 1500
          },
          "byAction": {
            "type": "object",
            "description": "Count by action type",
            "additionalProperties": {
              "type": "number"
            },
            "example": {
              "CREATE": 500,
              "UPDATE": 800,
              "DELETE": 200
            }
          },
          "byEntityType": {
            "type": "object",
            "description": "Count by entity type",
            "additionalProperties": {
              "type": "number"
            },
            "example": {
              "Device": 900,
              "User": 400,
              "Organization": 200
            }
          },
          "byUser": {
            "description": "Top users by activity",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserAuditCount"
            }
          },
          "successRate": {
            "type": "number",
            "description": "Success rate as a decimal (0-1)",
            "example": 0.95
          }
        },
        "required": [
          "totalCount",
          "byAction",
          "byEntityType",
          "byUser",
          "successRate"
        ]
      },
      "AuthEventDto": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "auth.login.success",
              "auth.login.failed",
              "auth.refresh.success",
              "auth.refresh.failed",
              "auth.logout",
              "auth.session_terminated_by_admin",
              "auth.federated_login.success",
              "auth.federated_login.failed"
            ],
            "description": "Canonical auth-audit event name (FM-494)."
          },
          "success": {
            "type": "boolean",
            "description": "Whether the underlying auth operation succeeded."
          },
          "provider": {
            "type": "string",
            "description": "OAuth/OIDC provider name when relevant (e.g. \"keycloak\", \"entra\")."
          },
          "errorCode": {
            "type": "string",
            "description": "Short error code for failed events (e.g. \"expired_token\")."
          },
          "errorDescription": {
            "type": "string",
            "description": "Human-readable error description for failed events."
          },
          "metadata": {
            "type": "object",
            "description": "Free-form metadata (small JSON object). MUST NOT contain access tokens."
          }
        },
        "required": [
          "event",
          "success"
        ]
      },
      "SystemRoleDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "System role record ID"
          },
          "userId": {
            "type": "string",
            "description": "User ID who has this role"
          },
          "userEmail": {
            "type": "string",
            "description": "User email"
          },
          "userName": {
            "type": "string",
            "description": "User name"
          },
          "role": {
            "type": "string",
            "enum": [
              "SUPERUSER",
              "SUPPORT"
            ],
            "description": "Role type"
          },
          "grantedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the role was granted"
          },
          "grantedById": {
            "type": "string",
            "description": "User ID who granted the role (null if granter was deleted)"
          },
          "grantedByEmail": {
            "type": "string",
            "description": "Email of user who granted the role (null if granter was deleted)"
          },
          "reason": {
            "type": "string",
            "description": "Reason for granting"
          },
          "revokedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the role was revoked (null if active)"
          },
          "revokedById": {
            "type": "string",
            "description": "User ID who revoked the role"
          },
          "revokedByEmail": {
            "type": "string",
            "description": "Email of user who revoked the role"
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the role is currently active"
          }
        },
        "required": [
          "id",
          "userId",
          "userEmail",
          "role",
          "grantedAt",
          "isActive"
        ]
      },
      "SystemRoleListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of system roles",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SystemRoleDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total count"
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether there are more items"
          }
        },
        "required": [
          "items",
          "total",
          "hasMore"
        ]
      },
      "UserSystemRolesDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User ID"
          },
          "isSuperuser": {
            "type": "boolean",
            "description": "Whether user is a superuser"
          },
          "isSupport": {
            "type": "boolean",
            "description": "Whether user has support role"
          },
          "activeRoles": {
            "description": "Active system roles",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SystemRoleDto"
            }
          }
        },
        "required": [
          "userId",
          "isSuperuser",
          "isSupport",
          "activeRoles"
        ]
      },
      "GrantSystemRoleDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User ID to grant the role to"
          },
          "role": {
            "type": "string",
            "enum": [
              "SUPERUSER",
              "SUPPORT"
            ],
            "description": "Role type to grant"
          },
          "reason": {
            "type": "string",
            "description": "Reason for granting the role"
          }
        },
        "required": [
          "userId",
          "role"
        ]
      },
      "RevokeSystemRoleDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User ID to revoke the role from"
          },
          "role": {
            "type": "string",
            "enum": [
              "SUPERUSER",
              "SUPPORT"
            ],
            "description": "Role type to revoke"
          },
          "reason": {
            "type": "string",
            "description": "Reason for revoking the role"
          }
        },
        "required": [
          "userId",
          "role"
        ]
      },
      "TenantSwitchRequiredErrorDto": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "example": 403
          },
          "error": {
            "type": "string",
            "example": "Forbidden"
          },
          "code": {
            "type": "string",
            "example": "TENANT_SWITCH_REQUIRED",
            "description": "Machine-readable code. Open a tenant switch with POST /api/v1/tenant/switch."
          },
          "message": {
            "type": "string",
            "example": "A tenant switch is required to act in this organization"
          },
          "organizationId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the request targeted and the caller may not act in yet"
          }
        },
        "required": [
          "statusCode",
          "error",
          "code",
          "message",
          "organizationId"
        ]
      },
      "TenantSwitchDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Switch record ID"
          },
          "userId": {
            "type": "string",
            "description": "User ID who performed the switch"
          },
          "fromOrgId": {
            "type": "string",
            "description": "Original organization ID"
          },
          "fromOrgName": {
            "type": "string",
            "description": "Original organization name"
          },
          "toOrgId": {
            "type": "string",
            "description": "Target organization ID"
          },
          "toOrgName": {
            "type": "string",
            "description": "Target organization name"
          },
          "duration": {
            "type": "string",
            "description": "Duration of the switch"
          },
          "reason": {
            "type": "string",
            "description": "Reason for switching"
          },
          "switchedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the switch was performed"
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the switch expires"
          },
          "returnedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the user returned to default tenant"
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the switch is currently active"
          }
        },
        "required": [
          "id",
          "userId",
          "toOrgId",
          "switchedAt",
          "isActive"
        ]
      },
      "TenantContextDto": {
        "type": "object",
        "properties": {
          "isSwitched": {
            "type": "boolean",
            "description": "Whether user is currently switched to another tenant"
          },
          "originalOrgId": {
            "type": "string",
            "description": "Original organization ID (if switched)"
          },
          "originalOrgName": {
            "type": "string",
            "description": "Original organization name (if switched)"
          },
          "currentOrgId": {
            "type": "string",
            "description": "Current organization ID"
          },
          "currentOrgName": {
            "type": "string",
            "description": "Current organization name"
          },
          "currentOrgSlug": {
            "type": "string",
            "description": "Current organization slug"
          },
          "originalOrgSlug": {
            "type": "string",
            "description": "Original organization slug (if switched)"
          },
          "activeSwitch": {
            "description": "Active switch details (if switched)",
            "allOf": [
              {
                "$ref": "#/components/schemas/TenantSwitchDto"
              }
            ]
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the switch expires (if switched)"
          },
          "currentRole": {
            "type": "string",
            "nullable": true,
            "description": "The caller's role in the current organization, from the database: admin, operator, viewer, a custom role name, support, or superadmin. Null when unresolved."
          },
          "permissions": {
            "description": "The caller's effective permissions in the current organization, as the server enforces them.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "isSwitched",
          "currentOrgId"
        ]
      },
      "SwitchTenantDto": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "string",
            "description": "Target organization ID to switch to",
            "format": "uuid"
          },
          "duration": {
            "type": "string",
            "enum": [
              "15m",
              "1h",
              "4h",
              "session"
            ],
            "description": "Duration of the switch",
            "default": "session"
          },
          "reason": {
            "type": "string",
            "description": "Required (3-500 characters after trimming) when switching into a tenant the caller is not a member of; ignored for a home tenant",
            "minLength": 3,
            "maxLength": 500
          },
          "fromOrganizationId": {
            "type": "string",
            "format": "uuid",
            "description": "Tenant the caller switches from. Must be a tenant the caller is a member of, or the target of the switch being replaced. Defaults to the current tenant, then the replaced switch target, then the primary membership."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Custom expiry (ISO 8601) for a switch into a tenant the caller is not a member of: at least 1 minute and at most 72 hours from now. Default: 8 hours. Not allowed together with duration; ignored for a home tenant."
          }
        },
        "required": [
          "organizationId"
        ]
      },
      "ExtendTenantSwitchDto": {
        "type": "object",
        "properties": {
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "New expiry (ISO 8601): later than the current expiry and at most 72 hours from now."
          },
          "reason": {
            "type": "string",
            "description": "Why the switch is extended (3-500 characters after trimming)",
            "minLength": 3,
            "maxLength": 500
          }
        },
        "required": [
          "expiresAt",
          "reason"
        ]
      },
      "MyTenantSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Organization ID"
          },
          "name": {
            "type": "string",
            "description": "Organization name"
          },
          "slug": {
            "type": "string",
            "description": "Organization slug"
          },
          "roleName": {
            "type": "string",
            "description": "Role name (null for superuser/support)"
          },
          "accessVia": {
            "type": "string",
            "description": "How the user has access: direct membership or via system role",
            "enum": [
              "member",
              "system"
            ]
          },
          "isDefault": {
            "type": "boolean",
            "description": "Whether this is the user's current/default organization"
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "accessVia",
          "isDefault"
        ]
      },
      "MyTenantListDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Tenants the user can access",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MyTenantSummaryDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total tenants"
          },
          "isSystemView": {
            "type": "boolean",
            "description": "True when items reflect a system-role view (all orgs); false when items only contain memberships"
          }
        },
        "required": [
          "items",
          "total",
          "isSystemView"
        ]
      },
      "TenantSwitchListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of tenant switches",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TenantSwitchDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total count"
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether there are more items"
          }
        },
        "required": [
          "items",
          "total",
          "hasMore"
        ]
      },
      "AdminActivityDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Activity ID"
          },
          "user": {
            "type": "object",
            "description": "User who performed the activity"
          },
          "targetUser": {
            "type": "object",
            "description": "Target user (if applicable)"
          },
          "activityType": {
            "type": "string",
            "description": "Activity type"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization ID"
          },
          "ipAddress": {
            "type": "string",
            "description": "IP address"
          },
          "userAgent": {
            "type": "string",
            "description": "User agent"
          },
          "description": {
            "type": "string",
            "description": "Description"
          },
          "success": {
            "type": "boolean",
            "description": "Success status"
          },
          "errorMessage": {
            "type": "string",
            "description": "Error message"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Created at"
          },
          "metadata": {
            "type": "object",
            "description": "Metadata"
          }
        },
        "required": [
          "id",
          "user",
          "activityType",
          "success",
          "createdAt"
        ]
      },
      "AdminActivityListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Activities",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdminActivityDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total count"
          },
          "limit": {
            "type": "number",
            "description": "Limit"
          },
          "offset": {
            "type": "number",
            "description": "Offset"
          }
        },
        "required": [
          "items",
          "total",
          "limit",
          "offset"
        ]
      },
      "TenantActivitySummaryDto": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "string",
            "description": "Organization ID"
          },
          "organizationName": {
            "type": "string",
            "description": "Organization name"
          },
          "totalActivities": {
            "type": "number",
            "description": "Total activities in period"
          },
          "activeUsers": {
            "type": "number",
            "description": "Active users (users with activity)"
          },
          "totalUsers": {
            "type": "number",
            "description": "Total users in organization"
          },
          "lastActivityAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last activity timestamp"
          },
          "activityBreakdown": {
            "type": "object",
            "description": "Activity breakdown by type"
          },
          "loginCount": {
            "type": "number",
            "description": "Login count in period"
          },
          "failedLoginCount": {
            "type": "number",
            "description": "Failed login count in period"
          },
          "daysSinceLastActivity": {
            "type": "number",
            "description": "Days since last activity"
          },
          "isInactive": {
            "type": "boolean",
            "description": "Is inactive (no activity in threshold days)"
          }
        },
        "required": [
          "organizationId",
          "organizationName",
          "totalActivities",
          "activeUsers",
          "totalUsers",
          "activityBreakdown",
          "loginCount",
          "failedLoginCount",
          "daysSinceLastActivity",
          "isInactive"
        ]
      },
      "InactivityReportDto": {
        "type": "object",
        "properties": {
          "thresholdDays": {
            "type": "number",
            "description": "Inactivity threshold in days"
          },
          "generatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Report generated at"
          },
          "inactiveTenants": {
            "description": "Inactive tenants",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TenantActivitySummaryDto"
            }
          },
          "totalInactiveTenants": {
            "type": "number",
            "description": "Total inactive tenants"
          },
          "totalTenants": {
            "type": "number",
            "description": "Total tenants checked"
          }
        },
        "required": [
          "thresholdDays",
          "generatedAt",
          "inactiveTenants",
          "totalInactiveTenants",
          "totalTenants"
        ]
      },
      "ExportActivityDto": {
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "string",
            "description": "Filter by organization ID"
          },
          "userId": {
            "type": "string",
            "description": "Filter by user ID"
          },
          "startDate": {
            "type": "string",
            "description": "Filter by start date"
          },
          "endDate": {
            "type": "string",
            "description": "Filter by end date"
          },
          "activityTypes": {
            "description": "Filter by activity types",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "format": {
            "type": "string",
            "description": "Export format",
            "enum": [
              "csv",
              "json"
            ],
            "default": "csv"
          },
          "maxRecords": {
            "type": "number",
            "description": "Maximum number of records",
            "default": 10000
          }
        }
      },
      "CaCertificateResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique CA certificate identifier (UUID)",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Human-readable name for the CA certificate",
            "example": "Production Root CA"
          },
          "fingerprint": {
            "type": "string",
            "description": "SHA-256 fingerprint of the certificate",
            "example": "A1:B2:C3:D4:E5:F6:A1:B2:C3:D4:E5:F6:A1:B2:C3:D4:E5:F6:A1:B2:C3:D4:E5:F6:A1:B2:C3:D4:E5:F6:A1:B2"
          },
          "isRoot": {
            "type": "boolean",
            "description": "Whether this is a root CA certificate",
            "example": true
          },
          "isDefault": {
            "type": "boolean",
            "description": "Whether this is the default signing CA",
            "example": true
          },
          "notBefore": {
            "format": "date-time",
            "type": "string",
            "description": "Certificate validity start date",
            "example": "2024-01-01T00:00:00Z"
          },
          "notAfter": {
            "format": "date-time",
            "type": "string",
            "description": "Certificate validity end date (expiration)",
            "example": "2034-01-01T00:00:00Z"
          },
          "retiredAt": {
            "format": "date-time",
            "type": "string",
            "description": "Date when the CA certificate was retired",
            "example": "2024-06-01T00:00:00Z"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Timestamp when the CA certificate was created",
            "example": "2024-01-01T00:00:00Z"
          },
          "subject": {
            "type": "string",
            "description": "Full subject distinguished name",
            "example": "CN=Root CA, O=logiccloud, C=US"
          },
          "issuer": {
            "type": "string",
            "description": "Full issuer distinguished name",
            "example": "CN=Root CA, O=logiccloud, C=US"
          },
          "serialNumber": {
            "type": "string",
            "description": "Certificate serial number (hex)",
            "example": "01:AB:CD:EF"
          },
          "keyAlgorithm": {
            "type": "string",
            "description": "Public key algorithm and size",
            "example": "RSA-4096"
          },
          "signatureAlgorithm": {
            "type": "string",
            "description": "Signature algorithm",
            "example": "SHA256withRSA"
          },
          "subjectAltNames": {
            "description": "Subject Alternative Names",
            "example": [
              "DNS:example.com",
              "IP:192.168.1.1"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "certificate": {
            "type": "string",
            "description": "PEM-encoded public certificate (safe to share)",
            "example": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
          }
        },
        "required": [
          "id",
          "name",
          "fingerprint",
          "isRoot",
          "isDefault",
          "notBefore",
          "notAfter",
          "createdAt"
        ]
      },
      "CaCertificateListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "List of CA certificates",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CaCertificateResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of CA certificates",
            "example": 5
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether there are more results available",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "description": "Cursor for the next page of results",
            "example": "550e8400-e29b-41d4-a716-446655440001",
            "format": "uuid"
          }
        },
        "required": [
          "data",
          "total",
          "hasMore"
        ]
      },
      "CreateCaCertificateDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable name for the CA certificate",
            "example": "Production Root CA",
            "maxLength": 255
          },
          "certificate": {
            "type": "string",
            "description": "PEM-encoded CA certificate",
            "example": "-----BEGIN CERTIFICATE-----\nMIIBkTCC...\n-----END CERTIFICATE-----"
          },
          "privateKey": {
            "type": "string",
            "description": "PEM-encoded private key (will be encrypted at rest)",
            "example": "-----BEGIN PRIVATE KEY-----\nMIIEvQIB...\n-----END PRIVATE KEY-----"
          },
          "passphrase": {
            "type": "string",
            "description": "Passphrase for encrypted private keys (PKCS#8 or legacy PEM encryption)",
            "example": "my-secret-passphrase"
          },
          "isRoot": {
            "type": "boolean",
            "description": "Whether this is a root CA certificate",
            "example": true,
            "default": false
          },
          "isDefault": {
            "type": "boolean",
            "description": "Set as the default signing CA for new device certificates",
            "example": false,
            "default": false
          }
        },
        "required": [
          "name",
          "certificate",
          "privateKey"
        ]
      },
      "GenerateCaCertificateDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable name for the CA certificate",
            "example": "Development Root CA",
            "maxLength": 255
          },
          "commonName": {
            "type": "string",
            "description": "Common Name (CN) for the certificate subject",
            "example": "logiccloud orchestrate Dev CA",
            "maxLength": 255
          },
          "organization": {
            "type": "string",
            "description": "Organization (O) for the certificate subject",
            "example": "logiccloud",
            "maxLength": 255
          },
          "country": {
            "type": "string",
            "description": "Country code (C) for the certificate subject (2-letter ISO 3166-1 alpha-2)",
            "example": "DE",
            "maxLength": 2
          },
          "validityYears": {
            "type": "number",
            "description": "Validity period in years (1-30)",
            "example": 10,
            "default": 10
          },
          "isDefault": {
            "type": "boolean",
            "description": "Set as the default signing CA for new device certificates",
            "example": false,
            "default": false
          }
        },
        "required": [
          "name",
          "commonName"
        ]
      },
      "RenewCaCertificateDto": {
        "type": "object",
        "properties": {
          "validityDays": {
            "type": "number",
            "description": "Validity of the re-issued certificate in days, starting now",
            "example": 365,
            "minimum": 1,
            "maximum": 3650,
            "default": 365
          }
        }
      },
      "CertificateListItemDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Certificate UUID",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "type": {
            "type": "string",
            "description": "Certificate type",
            "enum": [
              "device",
              "ca"
            ],
            "example": "device"
          },
          "subject": {
            "type": "string",
            "description": "Certificate subject (Common Name)",
            "example": "CN=device-001.example.com"
          },
          "issuer": {
            "type": "string",
            "description": "Certificate issuer",
            "example": "CN=Margo Root CA"
          },
          "fingerprint": {
            "type": "string",
            "description": "Certificate SHA-256 fingerprint",
            "example": "A1:B2:C3:D4:E5:F6..."
          },
          "notBefore": {
            "format": "date-time",
            "type": "string",
            "description": "Certificate validity start date",
            "example": "2024-01-01T00:00:00.000Z"
          },
          "notAfter": {
            "format": "date-time",
            "type": "string",
            "description": "Certificate expiration date",
            "example": "2025-01-01T00:00:00.000Z"
          },
          "status": {
            "type": "string",
            "description": "Certificate status",
            "enum": [
              "valid",
              "expiring",
              "expired",
              "revoked",
              "unknown"
            ],
            "example": "valid"
          },
          "validityKnown": {
            "type": "boolean",
            "description": "False for a legacy device certificate whose notBefore, notAfter and issuer were not read from a certificate. Its dates are placeholders and must not be shown or alerted on.",
            "example": true
          },
          "deviceId": {
            "type": "string",
            "description": "Device ID (only for device certificates)",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "deviceName": {
            "type": "string",
            "description": "Device name (only for device certificates)",
            "example": "Production Gateway 1"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Certificate creation date",
            "example": "2024-01-01T00:00:00.000Z"
          },
          "serialNumber": {
            "type": "string",
            "description": "Certificate serial number (hex)"
          },
          "keyAlgorithm": {
            "type": "string",
            "description": "Public key algorithm and size"
          },
          "signatureAlgorithm": {
            "type": "string",
            "description": "Signature algorithm"
          },
          "subjectAltNames": {
            "description": "Subject Alternative Names",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "certificate": {
            "type": "string",
            "description": "PEM-encoded public certificate (CA certs only)"
          }
        },
        "required": [
          "id",
          "type",
          "subject",
          "issuer",
          "fingerprint",
          "notBefore",
          "notAfter",
          "status",
          "validityKnown",
          "createdAt"
        ]
      },
      "CertificateListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "List of certificates",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CertificateListItemDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of certificates matching the filter",
            "example": 150
          },
          "page": {
            "type": "number",
            "description": "Current page number",
            "example": 1
          },
          "limit": {
            "type": "number",
            "description": "Number of items per page",
            "example": 20
          },
          "totalPages": {
            "type": "number",
            "description": "Total number of pages",
            "example": 8
          }
        },
        "required": [
          "data",
          "total",
          "page",
          "limit",
          "totalPages"
        ]
      },
      "CertificateStatsDto": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Total number of certificates",
            "example": 150
          },
          "valid": {
            "type": "number",
            "description": "Number of valid certificates (not expiring within 30 days)",
            "example": 120
          },
          "expiring": {
            "type": "number",
            "description": "Number of certificates expiring within 30 days",
            "example": 15
          },
          "expired": {
            "type": "number",
            "description": "Number of expired certificates",
            "example": 5
          },
          "unknown": {
            "type": "number",
            "description": "Number of non-revoked legacy device certificates whose validity is unknown (stored before the real certificate was kept). They are never counted as valid, expiring or expired.",
            "example": 953
          },
          "revoked": {
            "type": "number",
            "description": "Number of revoked certificates",
            "example": 10
          }
        },
        "required": [
          "total",
          "valid",
          "expiring",
          "expired",
          "unknown",
          "revoked"
        ]
      },
      "CreateGitRegistryDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Unique name for the registry within the organization",
            "example": "my-workloads-repo",
            "minLength": 2,
            "maxLength": 100
          },
          "url": {
            "type": "string",
            "description": "Git repository URL",
            "example": "https://github.com/org/workloads.git"
          },
          "branch": {
            "type": "string",
            "description": "Branch to use",
            "example": "main",
            "default": "main"
          },
          "path": {
            "type": "string",
            "description": "Path within the repository to scan for workloads",
            "example": "/workloads",
            "default": "/"
          },
          "authType": {
            "type": "string",
            "description": "Authentication type",
            "enum": [
              "NONE",
              "HTTPS_TOKEN",
              "SSH_KEY"
            ],
            "default": "NONE",
            "example": "NONE"
          },
          "credential": {
            "type": "string",
            "description": "Credential (token or SSH key) for authentication. Will be encrypted at rest.",
            "example": "ghp_xxxxxxxxxxxxxxxxxxxx"
          },
          "syncIntervalMinutes": {
            "type": "number",
            "description": "Sync interval in minutes",
            "example": 60,
            "default": 60,
            "minimum": 1,
            "maximum": 1440
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the registry is enabled",
            "default": true,
            "example": true
          }
        },
        "required": [
          "name",
          "url"
        ]
      },
      "GitRegistryResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization ID that owns this registry",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "name": {
            "type": "string",
            "description": "Registry name",
            "example": "my-workloads-repo"
          },
          "url": {
            "type": "string",
            "description": "Git repository URL",
            "example": "https://github.com/org/workloads.git"
          },
          "branch": {
            "type": "string",
            "description": "Branch to use",
            "example": "main"
          },
          "path": {
            "type": "string",
            "description": "Path within the repository to scan",
            "example": "/workloads"
          },
          "authType": {
            "type": "string",
            "description": "Authentication type",
            "enum": [
              "NONE",
              "HTTPS_TOKEN",
              "SSH_KEY"
            ],
            "example": "NONE"
          },
          "hasCredentials": {
            "type": "boolean",
            "description": "Whether credentials are configured",
            "example": false
          },
          "syncIntervalMinutes": {
            "type": "number",
            "description": "Sync interval in minutes",
            "example": 60
          },
          "lastSyncAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last successful sync timestamp",
            "example": "2024-01-15T10:30:00Z",
            "nullable": true
          },
          "lastSyncError": {
            "type": "string",
            "description": "Last sync error message",
            "example": "Authentication failed",
            "nullable": true
          },
          "lastSyncCommit": {
            "type": "string",
            "description": "Last synced commit SHA",
            "example": "abc123def456",
            "nullable": true
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the registry is enabled",
            "example": true
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Creation timestamp",
            "example": "2024-01-01T00:00:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last update timestamp",
            "example": "2024-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "organizationId",
          "name",
          "url",
          "branch",
          "path",
          "authType",
          "hasCredentials",
          "syncIntervalMinutes",
          "enabled",
          "createdAt",
          "updatedAt"
        ]
      },
      "GitRegistryListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of Git registries",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GitRegistryResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of registries matching the query",
            "example": 10
          },
          "limit": {
            "type": "number",
            "description": "Number of items returned",
            "example": 20
          },
          "offset": {
            "type": "number",
            "description": "Number of items skipped",
            "example": 0
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether there are more items",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "description": "Cursor for fetching next page",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          }
        },
        "required": [
          "items",
          "total",
          "limit",
          "offset",
          "hasMore"
        ]
      },
      "UpdateGitRegistryDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Unique name for the registry within the organization",
            "example": "my-workloads-repo",
            "minLength": 2,
            "maxLength": 100
          },
          "url": {
            "type": "string",
            "description": "Git repository URL",
            "example": "https://github.com/org/workloads.git"
          },
          "branch": {
            "type": "string",
            "description": "Branch to use",
            "example": "main"
          },
          "path": {
            "type": "string",
            "description": "Path within the repository to scan for workloads",
            "example": "/workloads"
          },
          "authType": {
            "type": "string",
            "description": "Authentication type",
            "enum": [
              "NONE",
              "HTTPS_TOKEN",
              "SSH_KEY"
            ],
            "example": "TOKEN"
          },
          "credential": {
            "type": "string",
            "description": "Credential (token or SSH key) for authentication. Set to null to remove.",
            "example": "ghp_xxxxxxxxxxxxxxxxxxxx",
            "nullable": true
          },
          "syncIntervalMinutes": {
            "type": "number",
            "description": "Sync interval in minutes",
            "example": 60,
            "minimum": 1,
            "maximum": 1440
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the registry is enabled",
            "example": true
          }
        }
      },
      "RejectedManifestDto": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "Path of the rejected manifest within the registry",
            "example": "applications/example-app/manifest.yaml"
          },
          "reason": {
            "type": "string",
            "description": "Why the manifest was rejected at ingest",
            "example": "REJECTED deploymentProfiles[0].type: type \"quadlet\" is not permitted by Margo v1.0.0-rc2"
          }
        },
        "required": [
          "path",
          "reason"
        ]
      },
      "GitRegistrySyncResultDto": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the sync operation completed successfully",
            "example": true
          },
          "registryId": {
            "type": "string",
            "description": "The ID of the registry that was synced",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "scope": {
            "type": "string",
            "description": "The scope of the sync (always \"tenant\" for tenant registries)",
            "example": "tenant",
            "enum": [
              "tenant"
            ]
          },
          "applicationsFound": {
            "type": "number",
            "description": "Number of applications found in the repository",
            "example": 5
          },
          "applicationsImported": {
            "type": "number",
            "description": "Number of new applications imported",
            "example": 2
          },
          "applicationsUpdated": {
            "type": "number",
            "description": "Number of existing applications updated",
            "example": 3
          },
          "errors": {
            "description": "Array of error messages encountered during sync",
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rejectedManifests": {
            "description": "Manifests rejected at ingest because every declared deployment profile failed rc2 admission (or none was declared). Distinct from errors — rejection is correct, intended behavior, not a sync failure — but must still be visible to the operator rather than showing up only as the application silently disappearing.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RejectedManifestDto"
            }
          },
          "syncDuration": {
            "type": "number",
            "description": "Duration of the sync operation in milliseconds",
            "example": 1234
          },
          "commit": {
            "type": "string",
            "description": "The Git commit SHA that was synced",
            "example": "abc123def456"
          }
        },
        "required": [
          "success",
          "registryId",
          "scope",
          "applicationsFound",
          "applicationsImported",
          "applicationsUpdated",
          "errors",
          "rejectedManifests",
          "syncDuration"
        ]
      },
      "CreateOciRegistryDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Unique name for the registry within the organization",
            "example": "my-container-registry",
            "minLength": 2,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "description": "Description of the registry",
            "example": "Production container images",
            "maxLength": 500
          },
          "url": {
            "type": "string",
            "description": "OCI registry URL",
            "example": "https://registry.example.com"
          },
          "registryType": {
            "type": "string",
            "description": "Registry type",
            "enum": [
              "GENERIC"
            ],
            "default": "GENERIC",
            "example": "GENERIC"
          },
          "authType": {
            "type": "string",
            "description": "Authentication type",
            "enum": [
              "NONE",
              "BASIC",
              "TOKEN"
            ],
            "default": "NONE",
            "example": "NONE"
          },
          "credential": {
            "type": "string",
            "description": "Credential for authentication. Will be encrypted at rest. For BASIC auth this MUST be \"username:password\". For TOKEN auth it is the raw token.",
            "example": "username:password"
          },
          "verifyTls": {
            "type": "boolean",
            "description": "Whether to verify TLS certificates",
            "default": true,
            "example": true
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the registry is enabled",
            "default": true,
            "example": true
          },
          "repositories": {
            "description": "Repositories to sync, e.g. `library/nginx`. Registry-side catalog enumeration is not used (FM-839): an empty list syncs nothing.",
            "example": [
              "library/nginx",
              "team/app"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "name",
          "url"
        ]
      },
      "OciRegistryResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization ID that owns this registry",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "name": {
            "type": "string",
            "description": "Registry name",
            "example": "my-container-registry"
          },
          "description": {
            "type": "string",
            "description": "Registry description",
            "example": "Production container images",
            "nullable": true
          },
          "url": {
            "type": "string",
            "description": "OCI registry URL",
            "example": "https://registry.example.com"
          },
          "registryType": {
            "type": "string",
            "description": "Registry type",
            "enum": [
              "GENERIC"
            ],
            "example": "GENERIC"
          },
          "authType": {
            "type": "string",
            "description": "Authentication type",
            "enum": [
              "NONE",
              "BASIC",
              "TOKEN"
            ],
            "example": "NONE"
          },
          "hasCredentials": {
            "type": "boolean",
            "description": "Whether credentials are configured",
            "example": false
          },
          "verifyTls": {
            "type": "boolean",
            "description": "Whether TLS verification is enabled",
            "example": true
          },
          "lastHealthCheck": {
            "format": "date-time",
            "type": "string",
            "description": "Last health check timestamp",
            "example": "2024-01-15T10:30:00Z",
            "nullable": true
          },
          "healthStatus": {
            "type": "string",
            "description": "Health status (healthy, unhealthy, unknown)",
            "example": "healthy",
            "nullable": true
          },
          "pullVerified": {
            "type": "string",
            "enum": [
              "TRUE",
              "FALSE",
              "NOT_APPLICABLE"
            ],
            "description": "FM-1265 (ADR-0016-1): pull verification, separate from healthStatus. TRUE: the probed repository (pullVerifiedRepository) is pullable, NOT every configured repository. FALSE: the registry is reachable and the credentials are accepted, but they grant no pull on that repository. NOT_APPLICABLE: no repository is configured. Null until first probed.",
            "example": "TRUE",
            "nullable": true
          },
          "pullVerifiedRepository": {
            "type": "string",
            "description": "The one repository the pull probe used (the first configured repository)",
            "example": "team/app",
            "nullable": true
          },
          "pullVerifiedDetail": {
            "type": "string",
            "description": "Why pullVerified is FALSE (status, scope, registry message)",
            "example": "The token server issued no token for scope repository:team/app:pull.",
            "nullable": true
          },
          "pullVerifiedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When pull access was last probed",
            "example": "2024-01-15T10:30:00Z",
            "nullable": true
          },
          "lastSyncAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last sync timestamp",
            "example": "2024-01-15T10:30:00Z",
            "nullable": true
          },
          "lastSyncError": {
            "type": "string",
            "description": "Last sync error message",
            "nullable": true
          },
          "syncIntervalMinutes": {
            "type": "number",
            "description": "Sync interval in minutes",
            "example": 60
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the registry is enabled",
            "example": true
          },
          "repositories": {
            "description": "Repositories this registry syncs, e.g. `library/nginx`. Registry-side catalog enumeration is not used (FM-839): an empty list syncs nothing.",
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Creation timestamp",
            "example": "2024-01-01T00:00:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last update timestamp",
            "example": "2024-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "organizationId",
          "name",
          "url",
          "registryType",
          "authType",
          "hasCredentials",
          "verifyTls",
          "syncIntervalMinutes",
          "enabled",
          "repositories",
          "createdAt",
          "updatedAt"
        ]
      },
      "OciRegistryListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "List of OCI registries",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OciRegistryResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of registries matching the query",
            "example": 10
          },
          "limit": {
            "type": "number",
            "description": "Number of items returned",
            "example": 20
          },
          "offset": {
            "type": "number",
            "description": "Number of items skipped",
            "example": 0
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether there are more items",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "description": "Cursor for fetching next page",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          }
        },
        "required": [
          "items",
          "total",
          "limit",
          "offset",
          "hasMore"
        ]
      },
      "UpdateOciRegistryDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Unique name for the registry within the organization",
            "example": "my-container-registry",
            "minLength": 2,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "description": "Description of the registry",
            "example": "Production container images",
            "maxLength": 500,
            "nullable": true
          },
          "url": {
            "type": "string",
            "description": "OCI registry URL",
            "example": "https://registry.example.com"
          },
          "registryType": {
            "type": "string",
            "description": "Registry type",
            "enum": [
              "GENERIC"
            ],
            "example": "GENERIC"
          },
          "authType": {
            "type": "string",
            "description": "Authentication type",
            "enum": [
              "NONE",
              "BASIC",
              "TOKEN"
            ],
            "example": "BASIC"
          },
          "credential": {
            "type": "string",
            "description": "Credential for authentication. Set to null to remove. For BASIC auth this MUST be \"username:password\". For TOKEN auth it is the raw token.",
            "example": "username:password",
            "nullable": true
          },
          "verifyTls": {
            "type": "boolean",
            "description": "Whether to verify TLS certificates",
            "example": true
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the registry is enabled",
            "example": true
          },
          "repositories": {
            "description": "Repositories to sync, e.g. `library/nginx`. Replaces the stored list. Registry-side catalog enumeration is not used (FM-839): an empty list syncs nothing.",
            "example": [
              "library/nginx",
              "team/app"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "OciRegistryHealthCheckResultDto": {
        "type": "object",
        "properties": {
          "healthy": {
            "type": "boolean",
            "description": "Whether the registry is healthy",
            "example": true
          },
          "status": {
            "type": "string",
            "description": "Health status of the registry",
            "example": "healthy",
            "enum": [
              "healthy",
              "unhealthy",
              "unknown"
            ]
          },
          "error": {
            "type": "string",
            "description": "Error message if the health check failed",
            "example": "Connection timed out (10s)"
          },
          "responseTimeMs": {
            "type": "number",
            "description": "Response time in milliseconds",
            "example": 150
          },
          "pull": {
            "type": "object",
            "description": "FM-1265: the pull verification of this check (absent when the probe could not decide)",
            "example": {
              "pullVerified": "FALSE",
              "pullVerifiedRepository": "team/app",
              "pullVerifiedDetail": null
            }
          }
        },
        "required": [
          "healthy",
          "status"
        ]
      },
      "OciRegistrySyncResultDto": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the sync operation completed successfully",
            "example": true
          },
          "registryId": {
            "type": "string",
            "description": "The ID of the registry that was synced",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "repositoriesFound": {
            "type": "number",
            "description": "Number of OCI repositories found in the registry",
            "example": 10
          },
          "applicationsFound": {
            "type": "number",
            "description": "Number of applications found in the registry",
            "example": 5
          },
          "applicationsImported": {
            "type": "number",
            "description": "Number of new applications imported",
            "example": 2
          },
          "applicationsUpdated": {
            "type": "number",
            "description": "Number of existing applications updated",
            "example": 3
          },
          "versionsRemoved": {
            "type": "number",
            "description": "Number of versions soft-deleted because no source provides them any more and they have no active deployments",
            "example": 0
          },
          "versionsMarkedUnavailable": {
            "type": "number",
            "description": "Number of versions kept but flagged SOURCE_UNAVAILABLE because an active deployment still references them",
            "example": 0
          },
          "applicationsRemoved": {
            "type": "number",
            "description": "Number of applications removed from the catalogue because their last version disappeared from the registry",
            "example": 0
          },
          "errors": {
            "description": "Array of error messages encountered during sync",
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "syncDuration": {
            "type": "number",
            "description": "Duration of the sync operation in milliseconds",
            "example": 1234
          }
        },
        "required": [
          "success",
          "registryId",
          "repositoriesFound",
          "applicationsFound",
          "applicationsImported",
          "applicationsUpdated",
          "versionsRemoved",
          "versionsMarkedUnavailable",
          "applicationsRemoved",
          "errors",
          "syncDuration"
        ]
      },
      "OciRegistrySyncRejectionDto": {
        "type": "object",
        "properties": {
          "tag": {
            "type": "string",
            "example": "1.0.0"
          },
          "code": {
            "type": "string",
            "example": "NON_SPEC_ARTIFACT_TYPE",
            "description": "Stable code: NON_SPEC_ARTIFACT_TYPE, NON_EMPTY_CONFIG, RESOURCE_MEDIA_TYPE_MISMATCH, MISSING_DESCRIPTION_LAYER, REMOVED_DISCOVERY_STRATEGY or TAG_VERSION_MISMATCH."
          },
          "reason": {
            "type": "string",
            "example": "OCI manifest has no artifactType; Margo rc2 requires \"application/vnd.margo.app.v1+json\"."
          }
        },
        "required": [
          "tag",
          "code",
          "reason"
        ]
      },
      "OciRegistryPerRepositorySyncHistoryDto": {
        "type": "object",
        "properties": {
          "repository": {
            "type": "string",
            "example": "library/nginx"
          },
          "tagCount": {
            "type": "number",
            "example": 12
          },
          "found": {
            "type": "number",
            "example": 3
          },
          "imported": {
            "type": "number",
            "example": 1
          },
          "updated": {
            "type": "number",
            "example": 2
          },
          "errors": {
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rejections": {
            "description": "Tags that were not imported because they do not conform to the Margo registry spec (FM-1713). Absent when nothing was rejected.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OciRegistrySyncRejectionDto"
            }
          }
        },
        "required": [
          "repository",
          "tagCount",
          "found",
          "imported",
          "updated",
          "errors"
        ]
      },
      "OciRegistrySyncHistoryEntryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "registryId": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "scope": {
            "type": "string",
            "enum": [
              "tenant",
              "global"
            ]
          },
          "organizationId": {
            "type": "object",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "partial",
              "failed"
            ]
          },
          "repositoriesFound": {
            "type": "number",
            "example": 10
          },
          "applicationsFound": {
            "type": "number",
            "example": 5
          },
          "applicationsImported": {
            "type": "number",
            "example": 2
          },
          "applicationsUpdated": {
            "type": "number",
            "example": 3
          },
          "errors": {
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "syncDurationMs": {
            "type": "number",
            "example": 1234
          },
          "perRepository": {
            "description": "Per-repository sync breakdown (FM-672)",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OciRegistryPerRepositorySyncHistoryDto"
            }
          },
          "triggeredBy": {
            "type": "object",
            "description": "User id (uuid) for manual runs, \"scheduler\" for cron, null when unknown",
            "example": "scheduler",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "example": "2026-06-08T12:34:56.000Z"
          }
        },
        "required": [
          "id",
          "registryId",
          "scope",
          "organizationId",
          "status",
          "repositoriesFound",
          "applicationsFound",
          "applicationsImported",
          "applicationsUpdated",
          "errors",
          "syncDurationMs",
          "perRepository",
          "triggeredBy",
          "createdAt"
        ]
      },
      "OciRegistrySyncHistoryListDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OciRegistrySyncHistoryEntryDto"
            }
          },
          "total": {
            "type": "number",
            "example": 42
          },
          "hasMore": {
            "type": "boolean",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "data",
          "total",
          "hasMore"
        ]
      },
      "FlecsStoreResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Store configuration ID",
            "format": "uuid"
          },
          "organizationId": {
            "type": "string",
            "description": "Owning organization ID",
            "format": "uuid"
          },
          "storeId": {
            "type": "number",
            "description": "FLECS store ID",
            "example": 89
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the store is active",
            "example": true
          },
          "lastSyncAt": {
            "type": "string",
            "nullable": true,
            "description": "When the catalog was last synced (discovery phase)",
            "format": "date-time"
          },
          "lastSyncError": {
            "type": "string",
            "nullable": true,
            "description": "Failure from the most recent discovery call (the store-id lookup itself). Does not reflect the outcome of the downstream OCI sync, which is tracked on the linked registry."
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Creation timestamp"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Last update timestamp"
          }
        },
        "required": [
          "id",
          "organizationId",
          "storeId",
          "enabled",
          "createdAt",
          "updatedAt"
        ]
      },
      "UpsertFlecsStoreDto": {
        "type": "object",
        "properties": {
          "storeId": {
            "type": "number",
            "description": "FLECS store ID. Acts as a catalog-visibility filter and, per FLECS, a mandatory anti-cross-tenant-pollution control. logiccloud is store 89. Note that a wrong store ID returns a plausible full catalog rather than an error.",
            "example": 89,
            "minimum": 1
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the store is active.",
            "default": false,
            "example": false
          }
        },
        "required": [
          "storeId"
        ]
      },
      "FlecsStorePerRepositorySyncHistoryDto": {
        "type": "object",
        "properties": {
          "repository": {
            "type": "string",
            "example": "catalog/com-example-gateway-app"
          },
          "tagCount": {
            "type": "number",
            "example": 12
          },
          "found": {
            "type": "number",
            "example": 3
          },
          "imported": {
            "type": "number",
            "example": 1
          },
          "updated": {
            "type": "number",
            "example": 2
          },
          "errors": {
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "skipped": {
            "type": "string",
            "enum": [
              "no-oci-artifact"
            ],
            "description": "Set when the app was skipped, not synced: `no-oci-artifact` means the FLECS store lists the app but it publishes no OCI artifact on the registry (404). Not an error (FM-1664)."
          },
          "rejections": {
            "description": "Tags that were not imported because they do not conform to the Margo registry spec (FM-1713). Absent when nothing was rejected.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OciRegistrySyncRejectionDto"
            }
          }
        },
        "required": [
          "repository",
          "tagCount",
          "found",
          "imported",
          "updated",
          "errors"
        ]
      },
      "FlecsStoreSyncResultDto": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "empty",
              "partial",
              "failed"
            ]
          },
          "registryId": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "repositoriesFound": {
            "type": "number",
            "example": 10
          },
          "applicationsFound": {
            "type": "number",
            "example": 6
          },
          "applicationsImported": {
            "type": "number",
            "example": 4
          },
          "applicationsUpdated": {
            "type": "number",
            "example": 2
          },
          "versionsRemoved": {
            "type": "number",
            "example": 0
          },
          "versionsMarkedUnavailable": {
            "type": "number",
            "example": 0
          },
          "applicationsRemoved": {
            "type": "number",
            "example": 0
          },
          "errors": {
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "perRepository": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlecsStorePerRepositorySyncHistoryDto"
            }
          },
          "syncDuration": {
            "type": "number",
            "example": 1234
          }
        },
        "required": [
          "success",
          "status",
          "registryId",
          "repositoriesFound",
          "applicationsFound",
          "applicationsImported",
          "applicationsUpdated",
          "versionsRemoved",
          "versionsMarkedUnavailable",
          "applicationsRemoved",
          "errors",
          "perRepository",
          "syncDuration"
        ]
      },
      "FlecsStoreRepositoryProgressDto": {
        "type": "object",
        "properties": {
          "repository": {
            "type": "string",
            "example": "catalog/com-example-gateway-app"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "checking",
              "added",
              "updated",
              "rejected"
            ]
          },
          "reason": {
            "type": "string",
            "description": "Present when status is \"rejected\" — why nothing was imported/updated.",
            "example": "no Margo application manifest found in this repository"
          }
        },
        "required": [
          "repository",
          "status"
        ]
      },
      "FlecsStoreSyncProgressDto": {
        "type": "object",
        "properties": {
          "registryId": {
            "type": "string",
            "nullable": true,
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "description": "null when no sync is currently running for this store"
          },
          "repositories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlecsStoreRepositoryProgressDto"
            }
          },
          "startedAt": {
            "type": "string",
            "nullable": true,
            "example": "2026-06-08T12:34:56.000Z"
          },
          "updatedAt": {
            "type": "string",
            "nullable": true,
            "example": "2026-06-08T12:34:58.000Z"
          }
        },
        "required": [
          "registryId",
          "repositories",
          "startedAt",
          "updatedAt"
        ]
      },
      "FlecsStoreSyncHistoryEntryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "storeId": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "scope": {
            "type": "string",
            "enum": [
              "flecs"
            ]
          },
          "organizationId": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "empty",
              "partial",
              "failed"
            ]
          },
          "repositoriesFound": {
            "type": "number",
            "example": 10
          },
          "applicationsFound": {
            "type": "number",
            "example": 6
          },
          "applicationsImported": {
            "type": "number",
            "example": 4
          },
          "applicationsUpdated": {
            "type": "number",
            "example": 2
          },
          "errors": {
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "syncDurationMs": {
            "type": "number",
            "example": 1234
          },
          "perRepository": {
            "description": "Per-repository imported/updated breakdown for this run",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlecsStorePerRepositorySyncHistoryDto"
            }
          },
          "triggeredBy": {
            "type": "string",
            "description": "User id (uuid) for manual runs, \"scheduler\" for cron, null when unknown",
            "example": "scheduler",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "example": "2026-06-08T12:34:56.000Z"
          }
        },
        "required": [
          "id",
          "storeId",
          "scope",
          "organizationId",
          "status",
          "repositoriesFound",
          "applicationsFound",
          "applicationsImported",
          "applicationsUpdated",
          "errors",
          "syncDurationMs",
          "perRepository",
          "triggeredBy",
          "createdAt"
        ]
      },
      "FlecsStoreSyncHistoryListDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FlecsStoreSyncHistoryEntryDto"
            }
          },
          "total": {
            "type": "number",
            "example": 12
          },
          "hasMore": {
            "type": "boolean",
            "example": false
          },
          "nextCursor": {
            "type": "string"
          }
        },
        "required": [
          "data",
          "total",
          "hasMore"
        ]
      },
      "SourceLinkDto": {
        "type": "object",
        "properties": {
          "registryType": {
            "type": "string",
            "description": "Registry type",
            "enum": [
              "git",
              "oci"
            ]
          },
          "registryId": {
            "type": "string",
            "description": "Registry ID"
          },
          "registryName": {
            "type": "string",
            "description": "Registry name"
          },
          "registryUrl": {
            "type": "string",
            "description": "Registry URL"
          },
          "lastSeenAt": {
            "type": "string",
            "description": "When the version was last seen in this registry"
          }
        },
        "required": [
          "registryType",
          "registryId",
          "registryName",
          "lastSeenAt"
        ]
      },
      "ApplicationDescriptionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the application description",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "applicationPackageId": {
            "type": "string",
            "description": "ID of the parent application package",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "applicationPackageName": {
            "type": "string",
            "description": "Name of the parent application package",
            "example": "Eclipse Mosquitto"
          },
          "version": {
            "type": "string",
            "description": "Version string (semver)",
            "example": "2.0.18"
          },
          "deploymentProfile": {
            "type": "string",
            "description": "Deployment profile type",
            "enum": [
              "DOCKER_COMPOSE",
              "HELM",
              "K8S_MANIFEST",
              "PODMAN",
              "QUADLET"
            ],
            "example": "HELM"
          },
          "manifest": {
            "type": "object",
            "description": "Full deployment manifest"
          },
          "checksumSha256": {
            "type": "string",
            "description": "SHA-256 checksum of the package",
            "example": "a1b2c3d4e5f6..."
          },
          "minMargoVersion": {
            "type": "string",
            "description": "Minimum Margo version required",
            "example": "1.0.0"
          },
          "compatibleDevices": {
            "description": "List of compatible device types/constraints",
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "resourceRequirements": {
            "type": "object",
            "description": "Resource requirements (CPU, memory, etc.)"
          },
          "validationErrors": {
            "description": "List of validation errors if any",
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "usesDeprecatedSchema": {
            "type": "boolean",
            "description": "True when the stored manifest still carries the retired packageLocation/keyLocation component-artifact fields instead of repository+revision (ADR-0010-1 / FM-1090). Such a description will not deploy until republished/resynced under the new schema.",
            "example": false
          },
          "usesRetiredParameterShape": {
            "type": "boolean",
            "description": "True when the stored manifest still carries the retired parameter shape (parameters[].type/required/validation/description, the array form of parameters, or configuration without schema). Such a description cannot be validated and will not deploy (409 RETIRED_PARAMETER_SHAPE) until republished under Margo rc2 (FM-1280).",
            "example": false
          },
          "lifecycleStatus": {
            "type": "string",
            "description": "Operator-set lifecycle status of this version (FM-1004). RETIRED versions cannot be selected for new deployments; DEPRECATED versions stay deployable.",
            "enum": [
              "ACTIVE",
              "DEPRECATED",
              "RETIRED"
            ],
            "example": "ACTIVE"
          },
          "availability": {
            "type": "string",
            "description": "Application availability status",
            "enum": [
              "AVAILABLE",
              "SOURCE_UNAVAILABLE"
            ],
            "example": "AVAILABLE"
          },
          "sourceLinks": {
            "description": "Source repository links for this version",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SourceLinkDto"
            }
          },
          "createdAt": {
            "type": "string",
            "description": "When this version was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "type": "string",
            "description": "When this version was last updated",
            "example": "2024-01-15T10:30:00Z"
          },
          "licenseFile": {
            "type": "string",
            "description": "FM-1473: license file path as declared by this version (catalog.application.licenseFile)",
            "example": "LICENSE.txt"
          },
          "licenseText": {
            "type": "string",
            "description": "FM-1473: this version's license content, when the file is text or Markdown",
            "example": "Apache License\nVersion 2.0, January 2004"
          },
          "releaseNotesFile": {
            "type": "string",
            "description": "FM-1473: release notes file path as declared by this version (catalog.application.releaseNotes)",
            "example": "release-notes.md"
          },
          "releaseNotesText": {
            "type": "string",
            "description": "FM-1473: this version's release notes, when the file is text or Markdown",
            "example": "## 12.4.10\n\n- Security fixes"
          },
          "tagline": {
            "type": "string",
            "description": "FM-1694: one-line tagline of this version (catalog.application.tagline)",
            "example": "Fast, reliable time-series storage"
          },
          "site": {
            "type": "string",
            "description": "FM-1694: application website, an absolute http(s) URL (catalog.application.site)",
            "example": "https://example.com/app"
          },
          "organizationSite": {
            "type": "string",
            "description": "FM-1694: organization website, an absolute http(s) URL (catalog.organization.site)",
            "example": "https://example.com"
          }
        },
        "required": [
          "id",
          "applicationPackageId",
          "applicationPackageName",
          "version",
          "deploymentProfile",
          "manifest",
          "compatibleDevices",
          "resourceRequirements",
          "validationErrors",
          "usesDeprecatedSchema",
          "usesRetiredParameterShape",
          "lifecycleStatus",
          "createdAt",
          "updatedAt"
        ]
      },
      "ApplicationWithLatestVersionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the application package",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization that owns this application",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          },
          "name": {
            "type": "string",
            "description": "Application name",
            "example": "Eclipse Mosquitto"
          },
          "description": {
            "type": "string",
            "description": "Application description",
            "example": "Lightweight MQTT broker for IoT"
          },
          "descriptionMarkdown": {
            "type": "string",
            "description": "Detailed markdown description of the application",
            "example": "# Mosquitto\n\nLightweight MQTT broker for IoT deployments."
          },
          "releaseNotes": {
            "type": "string",
            "description": "Release notes for the current version",
            "example": "## Version 2.0.18\n\n- Security fixes\n- Performance improvements"
          },
          "descriptionFile": {
            "type": "string",
            "description": "URL or path to detailed description file (Markdown)",
            "example": "/api/v1/applications/550e8400.../files/description"
          },
          "releaseNotesFile": {
            "type": "string",
            "description": "URL or path to release notes file (Markdown or PDF)",
            "example": "/api/v1/applications/550e8400.../files/release-notes"
          },
          "licenseFile": {
            "type": "string",
            "description": "URL or path to license file (text, Markdown, or PDF)",
            "example": "/api/v1/applications/550e8400.../files/license"
          },
          "licenseText": {
            "type": "string",
            "description": "Inline license text content",
            "example": "MIT License\n\nCopyright (c) 2024..."
          },
          "vendor": {
            "type": "string",
            "description": "Vendor/author name",
            "example": "Eclipse Foundation"
          },
          "category": {
            "type": "string",
            "description": "Category classification",
            "example": "IoT"
          },
          "iconUrl": {
            "type": "string",
            "description": "URL to application icon",
            "example": "/api/v1/applications/550e8400.../icon"
          },
          "availability": {
            "type": "string",
            "description": "Application availability status",
            "enum": [
              "AVAILABLE",
              "SOURCE_UNAVAILABLE"
            ],
            "example": "AVAILABLE"
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata"
          },
          "versionCount": {
            "type": "number",
            "description": "Number of versions available",
            "example": 3
          },
          "latestVersion": {
            "type": "string",
            "description": "Latest version string",
            "example": "2.0.18"
          },
          "createdAt": {
            "type": "string",
            "description": "When the package was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "type": "string",
            "description": "When the package was last updated",
            "example": "2024-01-15T10:30:00Z"
          },
          "latestDescription": {
            "description": "Details of the latest version",
            "allOf": [
              {
                "$ref": "#/components/schemas/ApplicationDescriptionDto"
              }
            ]
          },
          "versionLifecycleStatuses": {
            "type": "array",
            "description": "Distinct lifecycle statuses of the application's versions (FM-1004). Set on list responses.",
            "items": {
              "type": "string",
              "enum": [
                "ACTIVE",
                "DEPRECATED",
                "RETIRED"
              ]
            }
          }
        },
        "required": [
          "id",
          "organizationId",
          "name",
          "metadata",
          "versionCount",
          "createdAt",
          "updatedAt"
        ]
      },
      "ApplicationListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "List of applications",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApplicationWithLatestVersionDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of applications matching the query",
            "example": 42
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether there are more results available",
            "example": true
          },
          "nextCursor": {
            "type": "string",
            "description": "Cursor for fetching the next page",
            "example": "eyJpZCI6IjEyMzQ1In0="
          }
        },
        "required": [
          "data",
          "total",
          "hasMore"
        ]
      },
      "CategoriesResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "List of unique categories",
            "example": [
              "IoT",
              "Database",
              "Monitoring"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "VendorsResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "List of unique vendors",
            "example": [
              "Eclipse Foundation",
              "Apache",
              "HashiCorp"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "ApplicationPackageDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the application package",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "organizationId": {
            "type": "string",
            "description": "Organization that owns this application",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          },
          "name": {
            "type": "string",
            "description": "Application name",
            "example": "Eclipse Mosquitto"
          },
          "description": {
            "type": "string",
            "description": "Application description",
            "example": "Lightweight MQTT broker for IoT"
          },
          "descriptionMarkdown": {
            "type": "string",
            "description": "Detailed markdown description of the application",
            "example": "# Mosquitto\n\nLightweight MQTT broker for IoT deployments."
          },
          "releaseNotes": {
            "type": "string",
            "description": "Release notes for the current version",
            "example": "## Version 2.0.18\n\n- Security fixes\n- Performance improvements"
          },
          "descriptionFile": {
            "type": "string",
            "description": "URL or path to detailed description file (Markdown)",
            "example": "/api/v1/applications/550e8400.../files/description"
          },
          "releaseNotesFile": {
            "type": "string",
            "description": "URL or path to release notes file (Markdown or PDF)",
            "example": "/api/v1/applications/550e8400.../files/release-notes"
          },
          "licenseFile": {
            "type": "string",
            "description": "URL or path to license file (text, Markdown, or PDF)",
            "example": "/api/v1/applications/550e8400.../files/license"
          },
          "licenseText": {
            "type": "string",
            "description": "Inline license text content",
            "example": "MIT License\n\nCopyright (c) 2024..."
          },
          "vendor": {
            "type": "string",
            "description": "Vendor/author name",
            "example": "Eclipse Foundation"
          },
          "category": {
            "type": "string",
            "description": "Category classification",
            "example": "IoT"
          },
          "iconUrl": {
            "type": "string",
            "description": "URL to application icon",
            "example": "/api/v1/applications/550e8400.../icon"
          },
          "availability": {
            "type": "string",
            "description": "Application availability status",
            "enum": [
              "AVAILABLE",
              "SOURCE_UNAVAILABLE"
            ],
            "example": "AVAILABLE"
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata"
          },
          "versionCount": {
            "type": "number",
            "description": "Number of versions available",
            "example": 3
          },
          "latestVersion": {
            "type": "string",
            "description": "Latest version string",
            "example": "2.0.18"
          },
          "createdAt": {
            "type": "string",
            "description": "When the package was created",
            "example": "2024-01-15T10:30:00Z"
          },
          "updatedAt": {
            "type": "string",
            "description": "When the package was last updated",
            "example": "2024-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "organizationId",
          "name",
          "metadata",
          "versionCount",
          "createdAt",
          "updatedAt"
        ]
      },
      "ApplicationDetailsResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "Application package details with all versions",
            "allOf": [
              {
                "$ref": "#/components/schemas/ApplicationPackageDto"
              }
            ]
          }
        },
        "required": [
          "data"
        ]
      },
      "ApplicationVersionsResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "List of application versions",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApplicationDescriptionDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of versions",
            "example": 5
          }
        },
        "required": [
          "data",
          "total"
        ]
      },
      "UpdateApplicationLifecycleStatusDto": {
        "type": "object",
        "properties": {
          "lifecycleStatus": {
            "type": "string",
            "description": "New lifecycle status of the version. RETIRED blocks new deployments of the version; existing deployments are unaffected. DEPRECATED stays deployable.",
            "enum": [
              "ACTIVE",
              "DEPRECATED",
              "RETIRED"
            ],
            "example": "DEPRECATED"
          }
        },
        "required": [
          "lifecycleStatus"
        ]
      },
      "MetricSnapshotDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Snapshot ID"
          },
          "deviceId": {
            "type": "string",
            "description": "Device ID"
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp of the snapshot"
          },
          "cpuUsagePercent": {
            "type": "object",
            "description": "CPU usage percentage (0-100)"
          },
          "memoryUsedBytes": {
            "type": "object",
            "description": "Memory used in bytes"
          },
          "memoryTotalBytes": {
            "type": "object",
            "description": "Memory total in bytes"
          },
          "memoryUsagePercent": {
            "type": "object",
            "description": "Memory usage percentage (computed)"
          },
          "diskUsedBytes": {
            "type": "object",
            "description": "Disk used in bytes"
          },
          "diskTotalBytes": {
            "type": "object",
            "description": "Disk total in bytes"
          },
          "diskUsagePercent": {
            "type": "object",
            "description": "Disk usage percentage (computed)"
          },
          "uptimeSeconds": {
            "type": "object",
            "description": "System uptime in seconds"
          },
          "agentUptimeSeconds": {
            "type": "object",
            "description": "Agent process uptime in seconds (absent for agents that do not report it)"
          },
          "restartCount": {
            "type": "object",
            "description": "Number of restarts"
          },
          "containersRunning": {
            "type": "object",
            "description": "Running containers count"
          },
          "containersFailed": {
            "type": "object",
            "description": "Failed containers count"
          },
          "errorCount": {
            "type": "number",
            "description": "Error count in the aggregation window"
          }
        },
        "required": [
          "id",
          "deviceId",
          "timestamp"
        ]
      },
      "TelemetryListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetricSnapshotDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total number of snapshots in range"
          }
        },
        "required": [
          "data",
          "total"
        ]
      },
      "RawMetricDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Full VictoriaMetrics metric name, e.g. fm_device_disk_used_bytes"
          },
          "mountpoint": {
            "type": "string",
            "description": "Filesystem mountpoint of a disk metric (FM-1229). Disk metrics have one entry per real filesystem; absent for every other metric."
          },
          "value": {
            "type": "number",
            "description": "Latest sample value"
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp of the latest sample (ISO 8601)"
          }
        },
        "required": [
          "name",
          "value",
          "timestamp"
        ]
      },
      "RawMetricsResponseDto": {
        "type": "object",
        "properties": {
          "metrics": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RawMetricDto"
            }
          }
        },
        "required": [
          "metrics"
        ]
      },
      "LogEntryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Log entry ID"
          },
          "deviceId": {
            "type": "string",
            "description": "Device ID"
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp"
          },
          "severity": {
            "type": "string",
            "description": "Severity level",
            "enum": [
              "DEBUG",
              "INFO",
              "WARN",
              "ERROR"
            ]
          },
          "body": {
            "type": "string",
            "description": "Log message body"
          },
          "source": {
            "type": "object",
            "description": "Log source (container name, service, or system)"
          },
          "attributes": {
            "type": "object",
            "description": "Structured attributes"
          }
        },
        "required": [
          "id",
          "deviceId",
          "timestamp",
          "severity",
          "body"
        ]
      },
      "LogListResponseDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LogEntryDto"
            }
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether there are more entries before the cursor"
          }
        },
        "required": [
          "data",
          "hasMore"
        ]
      },
      "LogSourcesResponseDto": {
        "type": "object",
        "properties": {
          "sources": {
            "description": "Distinct workload/container/service `source` values seen for this device within the log retention window, sorted alphabetically. Populates the workload picker without the caller having to scan the full log.",
            "example": [
              "edge-agent",
              "my-container",
              "system"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "sources"
        ]
      },
      "DeviceHealthSummaryDto": {
        "type": "object",
        "properties": {
          "deviceId": {
            "type": "string"
          },
          "deviceName": {
            "type": "string"
          },
          "cpuUsagePercent": {
            "type": "object"
          },
          "memoryUsagePercent": {
            "type": "object"
          },
          "diskUsagePercent": {
            "type": "object"
          }
        },
        "required": [
          "deviceId",
          "deviceName"
        ]
      },
      "RecentErrorDto": {
        "type": "object",
        "properties": {
          "deviceId": {
            "type": "string"
          },
          "deviceName": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "timestamp": {
            "type": "string"
          }
        },
        "required": [
          "deviceId",
          "deviceName",
          "message",
          "timestamp"
        ]
      },
      "FleetHealthResponseDto": {
        "type": "object",
        "properties": {
          "totalDevices": {
            "type": "number",
            "description": "Total devices in organization"
          },
          "onlineDevices": {
            "type": "number",
            "description": "Devices currently online"
          },
          "offlineDevices": {
            "type": "number",
            "description": "Devices currently offline"
          },
          "errorDevices": {
            "type": "number",
            "description": "Devices in error state"
          },
          "avgCpuPercent": {
            "type": "object",
            "description": "Average CPU usage across fleet (%)"
          },
          "avgMemPercent": {
            "type": "object",
            "description": "Average memory usage across fleet (%)"
          },
          "avgDiskPercent": {
            "type": "object",
            "description": "Average disk usage across fleet (%)"
          },
          "devicesWithHighCpu": {
            "description": "Devices with CPU usage above 90%",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceHealthSummaryDto"
            }
          },
          "devicesLowDisk": {
            "description": "Devices with disk usage above 85%",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceHealthSummaryDto"
            }
          },
          "recentErrors": {
            "description": "Recent error log entries across fleet",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecentErrorDto"
            }
          }
        },
        "required": [
          "totalDevices",
          "onlineDevices",
          "offlineDevices",
          "errorDevices",
          "devicesWithHighCpu",
          "devicesLowDisk",
          "recentErrors"
        ]
      },
      "WsTicketResponseDto": {
        "type": "object",
        "properties": {
          "ticket": {
            "type": "string",
            "description": "Opaque single-use handshake ticket. Pass it as the `ticket` query parameter on the WebSocket upgrade. It carries no claims and is consumed on first redemption.",
            "example": "a3f1c0de9b2447e8a1d6f05c7b3e9821a3f1c0de9b2447e8a1d6f05c7b3e9821"
          },
          "expiresIn": {
            "type": "number",
            "description": "Ticket lifetime in seconds. Redeem it immediately; it is not a session token.",
            "example": 60
          }
        },
        "required": [
          "ticket",
          "expiresIn"
        ]
      },
      "AlertRuleTargetSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "example": "Ops on-call email"
          },
          "channel": {
            "type": "string",
            "example": "EMAIL"
          },
          "enabled": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "name",
          "channel",
          "enabled"
        ]
      },
      "AlertSuppressionResponseDto": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "AFTER_FIRST",
              "RATE"
            ]
          },
          "maxNotifications": {
            "type": "object",
            "nullable": true,
            "description": "RATE trigger only. Null for AFTER_FIRST."
          },
          "intervalSeconds": {
            "type": "object",
            "nullable": true,
            "description": "RATE trigger only. Null for AFTER_FIRST."
          },
          "liftSchedule": {
            "type": "object",
            "additionalProperties": true,
            "description": "The structured lift schedule as stored — never a cron expression. See `AlertSuppressionDto.liftSchedule` for the three admitted shapes.",
            "example": {
              "type": "dailyAt",
              "time": "23:59"
            }
          },
          "liftTimezone": {
            "type": "string",
            "example": "Europe/Berlin"
          },
          "liftMode": {
            "type": "string",
            "enum": [
              "DIGEST",
              "REPEAT",
              "SILENT"
            ]
          }
        },
        "required": [
          "enabled",
          "trigger",
          "liftSchedule",
          "liftTimezone",
          "liftMode"
        ]
      },
      "AlertRuleResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "example": "CPU above 80% on edge nodes"
          },
          "ruleType": {
            "type": "string",
            "enum": [
              "DEVICE_OFFLINE",
              "METRIC_THRESHOLD",
              "WORKLOAD_CRASH",
              "ROLLOUT_HALTED"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "GLOBAL",
              "LABEL",
              "DEVICE",
              "DEPLOYMENT"
            ]
          },
          "targetId": {
            "type": "object",
            "format": "uuid",
            "nullable": true
          },
          "targetLabels": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "expression": {
            "type": "object",
            "example": "cpu > 80 for 5m",
            "nullable": true
          },
          "thresholdMinutes": {
            "type": "object",
            "nullable": true
          },
          "severity": {
            "type": "string",
            "enum": [
              "INFO",
              "WARNING",
              "CRITICAL"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "notificationTargets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AlertRuleTargetSummaryDto"
            }
          },
          "suppression": {
            "nullable": true,
            "description": "Notification suppression for this rule (FM-1269), or null when none is set.",
            "type": "object",
            "allOf": [
              {
                "$ref": "#/components/schemas/AlertSuppressionResponseDto"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "ruleType",
          "scope",
          "targetLabels",
          "severity",
          "enabled",
          "notificationTargets",
          "createdAt",
          "updatedAt"
        ]
      },
      "AlertDeliveryResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "ruleId": {
            "type": "string",
            "format": "uuid"
          },
          "ruleName": {
            "type": "string",
            "example": "CPU above 80% on edge nodes"
          },
          "deviceId": {
            "type": "object",
            "format": "uuid",
            "nullable": true
          },
          "notificationTargetId": {
            "type": "string",
            "format": "uuid"
          },
          "notificationTargetName": {
            "type": "string",
            "example": "Ops on-call email"
          },
          "channel": {
            "type": "string",
            "example": "EMAIL"
          },
          "state": {
            "type": "string",
            "enum": [
              "OPEN",
              "RESOLVED"
            ]
          },
          "firedAt": {
            "type": "string",
            "format": "date-time"
          },
          "resolvedAt": {
            "type": "object",
            "format": "date-time",
            "nullable": true
          },
          "deliveredAt": {
            "type": "object",
            "format": "date-time",
            "nullable": true,
            "description": "When the transport confirmed delivery. Null while pending or failed."
          },
          "error": {
            "type": "object",
            "nullable": true,
            "description": "Transport failure detail after the retry budget was exhausted."
          },
          "suppressedAt": {
            "type": "object",
            "format": "date-time",
            "nullable": true,
            "description": "When notification suppression withheld the push for this occurrence (FM-1269). This is the third outcome, distinct from the other two: pending has every timestamp field null; failed has error set; suppressed has this field set instead. deliveredAt and suppressedAt are never both set — a push either went out or it did not."
          }
        },
        "required": [
          "id",
          "ruleId",
          "ruleName",
          "notificationTargetId",
          "notificationTargetName",
          "channel",
          "state",
          "firedAt"
        ]
      },
      "AlertDeliveryListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AlertDeliveryResponseDto"
            }
          },
          "total": {
            "type": "number",
            "description": "Total matching records, ignoring limit/offset",
            "example": 128
          }
        },
        "required": [
          "items",
          "total"
        ]
      },
      "AlertMetricCatalogEntryDto": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "The DSL identifier, exactly as written in an expression.",
            "example": "cpu"
          },
          "kind": {
            "type": "string",
            "description": "`metric`: a telemetry value; `attribute`: a device property.",
            "enum": [
              "metric",
              "attribute"
            ]
          },
          "valueType": {
            "type": "string",
            "enum": [
              "number",
              "string"
            ]
          },
          "unit": {
            "type": "string",
            "description": "`percent` fields (cpu, memory, disk) compare against 0–100, never bytes.",
            "enum": [
              "percent",
              "count",
              "seconds",
              "none"
            ]
          },
          "operators": {
            "type": "array",
            "description": "The comparison operators the parser accepts for this field.",
            "example": [
              "=",
              "!="
            ],
            "items": {
              "type": "string",
              "enum": [
                ">",
                ">=",
                "<",
                "<=",
                "=",
                "!="
              ]
            }
          },
          "allowedValues": {
            "description": "For enumerated attributes (`device.status`): the only values that can match.",
            "example": [
              "ONLINE",
              "OFFLINE",
              "PENDING",
              "ERROR"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "keyPrefix": {
            "type": "string",
            "description": "Set on the `device.label.<key>` entries: the prefix the label key follows. Any key under this prefix is accepted on write, not only the ones listed.",
            "example": "device.label."
          }
        },
        "required": [
          "field",
          "kind",
          "valueType",
          "unit",
          "operators"
        ]
      },
      "AlertMetricCatalogResponseDto": {
        "type": "object",
        "properties": {
          "fields": {
            "description": "Every field an expression may name, including one `device.label.<key>` entry per label key the caller's organization's devices carry.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AlertMetricCatalogEntryDto"
            }
          },
          "labelKeys": {
            "description": "The caller's organization's device label keys, as used in the label entries.",
            "example": [
              "environment",
              "site"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "fields",
          "labelKeys"
        ]
      },
      "AlertSuppressionDto": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether suppression is active for this rule. Defaults to true.",
            "default": true
          },
          "trigger": {
            "type": "string",
            "description": "What opens the suppression window. AFTER_FIRST opens as soon as the first notification for the scope selection has pushed; RATE opens once maxNotifications have pushed within intervalSeconds. Defaults to AFTER_FIRST.",
            "enum": [
              "AFTER_FIRST",
              "RATE"
            ],
            "default": "AFTER_FIRST"
          },
          "maxNotifications": {
            "type": "number",
            "description": "RATE trigger only: how many notifications pass per intervalSeconds before the window opens. Required together with intervalSeconds when trigger is RATE; rejected when trigger is AFTER_FIRST.",
            "minimum": 1,
            "maximum": 1000
          },
          "intervalSeconds": {
            "type": "number",
            "description": "RATE trigger only: the interval, in seconds, that the maxNotifications budget applies to. 60 seconds to 604800 seconds (one week).",
            "minimum": 60,
            "maximum": 604800
          },
          "liftSchedule": {
            "type": "object",
            "description": "Structured lift schedule for when the suppression window opens — NEVER a cron expression (ADR-0018-1 §2.4). Exactly one of: {\"type\":\"everyHours\",\"hours\":1..168}, {\"type\":\"dailyAt\",\"time\":\"HH:MM\"} (24-hour, zero-padded), {\"type\":\"weeklyAt\",\"days\":[\"MON\"..\"SUN\"],\"time\":\"HH:MM\"}. Validated against this exact shape server-side; an unknown type, a missing or extra field, or a malformed value is rejected with a 400 naming the field.",
            "additionalProperties": true,
            "example": {
              "type": "dailyAt",
              "time": "23:59"
            }
          },
          "liftTimezone": {
            "type": "string",
            "description": "IANA time zone the lift schedule is read in, e.g. Europe/Berlin or UTC. Defaults to UTC.",
            "default": "UTC",
            "maxLength": 64,
            "example": "Europe/Berlin"
          },
          "liftMode": {
            "type": "string",
            "description": "What the scheduled lift produces. DIGEST sends one summary notification naming every occurrence suppressed during the window (the default). REPEAT re-notifies every occurrence still open at lift time. SILENT sends nothing — the delivery history is the only record.",
            "enum": [
              "DIGEST",
              "REPEAT",
              "SILENT"
            ],
            "default": "DIGEST"
          }
        },
        "required": [
          "liftSchedule"
        ]
      },
      "CreateAlertRuleDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable rule name",
            "example": "CPU above 80% on edge nodes"
          },
          "ruleType": {
            "type": "string",
            "description": "Condition family the rule evaluates",
            "enum": [
              "DEVICE_OFFLINE",
              "METRIC_THRESHOLD",
              "WORKLOAD_CRASH",
              "ROLLOUT_HALTED"
            ],
            "example": "METRIC_THRESHOLD"
          },
          "scope": {
            "type": "string",
            "description": "Which devices the rule applies to. GLOBAL covers every device in the organization; LABEL matches the selector in targetLabels; DEVICE matches the single device in targetId. ROLLOUT_HALTED rules take GLOBAL (every deployment of the organization) or DEPLOYMENT (the single deployment in targetId); the device scopes are rejected for them, and DEPLOYMENT is rejected for every other rule type.",
            "enum": [
              "GLOBAL",
              "LABEL",
              "DEVICE",
              "DEPLOYMENT"
            ],
            "default": "GLOBAL"
          },
          "targetId": {
            "type": "string",
            "description": "Device id when scope is DEVICE, deployment id when scope is DEPLOYMENT (it must be a deployment of the caller’s organization); required for both, ignored otherwise. On PATCH, null clears it (rejected while the resulting scope needs it).",
            "format": "uuid",
            "nullable": true
          },
          "targetLabels": {
            "type": "object",
            "description": "Label selector. Required when scope is LABEL, ignored otherwise. A device matches when every key/value pair is present on its labels.",
            "additionalProperties": {
              "type": "string"
            },
            "example": {
              "site": "berlin",
              "tier": "edge"
            }
          },
          "expression": {
            "type": "string",
            "description": "Condition expression. Required for METRIC_THRESHOLD and WORKLOAD_CRASH; rejected for DEVICE_OFFLINE (which uses thresholdMinutes) and ROLLOUT_HALTED (which has no condition). Syntax: `field op value [AND field op value ...] [for <duration>]`, where duration is e.g. 30s, 5m, 2h. The field vocabulary (names, units, admitted operators, allowed values) is served by `GET /api/v1/alert-rules/metric-catalog`; an expression naming any other field is rejected with 400 at write time.",
            "example": "cpu > 80 AND device.label.tier = \"edge\" for 5m"
          },
          "thresholdMinutes": {
            "type": "number",
            "description": "DEVICE_OFFLINE only: minutes without a heartbeat before the rule fires. Defaults to 5 per FR-FM-021.",
            "minimum": 1,
            "maximum": 1440,
            "default": 5,
            "nullable": true
          },
          "severity": {
            "type": "string",
            "description": "Operator-assigned severity, carried verbatim into every notification the rule produces (email subject and body, webhook payload). Defaults to WARNING.",
            "enum": [
              "INFO",
              "WARNING",
              "CRITICAL"
            ],
            "default": "WARNING"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the rule is evaluated",
            "default": true
          },
          "notificationTargetIds": {
            "description": "Notification targets to deliver this rule’s alerts to. Ids belonging to another organization are rejected.",
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "suppression": {
            "description": "Notification suppression for this rule (FM-1269). Omit entirely for no suppression — today’s behaviour, unchanged: every occurrence notifies.",
            "allOf": [
              {
                "$ref": "#/components/schemas/AlertSuppressionDto"
              }
            ]
          }
        },
        "required": [
          "name",
          "ruleType"
        ]
      },
      "UpdateAlertRuleDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable rule name",
            "example": "CPU above 80% on edge nodes"
          },
          "ruleType": {
            "type": "string",
            "description": "Condition family the rule evaluates",
            "enum": [
              "DEVICE_OFFLINE",
              "METRIC_THRESHOLD",
              "WORKLOAD_CRASH",
              "ROLLOUT_HALTED"
            ],
            "example": "METRIC_THRESHOLD"
          },
          "scope": {
            "type": "string",
            "description": "Which devices the rule applies to. GLOBAL covers every device in the organization; LABEL matches the selector in targetLabels; DEVICE matches the single device in targetId. ROLLOUT_HALTED rules take GLOBAL (every deployment of the organization) or DEPLOYMENT (the single deployment in targetId); the device scopes are rejected for them, and DEPLOYMENT is rejected for every other rule type.",
            "enum": [
              "GLOBAL",
              "LABEL",
              "DEVICE",
              "DEPLOYMENT"
            ],
            "default": "GLOBAL"
          },
          "targetId": {
            "type": "string",
            "description": "Device id when scope is DEVICE, deployment id when scope is DEPLOYMENT (it must be a deployment of the caller’s organization); required for both, ignored otherwise. On PATCH, null clears it (rejected while the resulting scope needs it).",
            "format": "uuid",
            "nullable": true
          },
          "targetLabels": {
            "type": "object",
            "description": "Label selector. Required when scope is LABEL, ignored otherwise. A device matches when every key/value pair is present on its labels.",
            "additionalProperties": {
              "type": "string"
            },
            "example": {
              "site": "berlin",
              "tier": "edge"
            }
          },
          "expression": {
            "type": "string",
            "description": "Condition expression. Required for METRIC_THRESHOLD and WORKLOAD_CRASH; rejected for DEVICE_OFFLINE (which uses thresholdMinutes) and ROLLOUT_HALTED (which has no condition). Syntax: `field op value [AND field op value ...] [for <duration>]`, where duration is e.g. 30s, 5m, 2h. The field vocabulary (names, units, admitted operators, allowed values) is served by `GET /api/v1/alert-rules/metric-catalog`; an expression naming any other field is rejected with 400 at write time.",
            "example": "cpu > 80 AND device.label.tier = \"edge\" for 5m"
          },
          "thresholdMinutes": {
            "type": "number",
            "description": "DEVICE_OFFLINE only: minutes without a heartbeat before the rule fires. Defaults to 5 per FR-FM-021.",
            "minimum": 1,
            "maximum": 1440,
            "default": 5,
            "nullable": true
          },
          "severity": {
            "type": "string",
            "description": "Operator-assigned severity, carried verbatim into every notification the rule produces (email subject and body, webhook payload). Defaults to WARNING.",
            "enum": [
              "INFO",
              "WARNING",
              "CRITICAL"
            ],
            "default": "WARNING"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the rule is evaluated",
            "default": true
          },
          "notificationTargetIds": {
            "description": "Notification targets to deliver this rule’s alerts to. Ids belonging to another organization are rejected.",
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "suppression": {
            "description": "Notification suppression patch (FM-1269). Omit to leave the current suppression untouched. Send null to remove it entirely — the rule reverts to pre-FM-1269 behaviour and notifies on every occurrence again. Send an object to replace it wholesale; a PATCH never merges individual suppression fields.",
            "nullable": true,
            "type": "object",
            "allOf": [
              {
                "$ref": "#/components/schemas/AlertSuppressionDto"
              }
            ]
          }
        }
      },
      "TestFireTargetResultDto": {
        "type": "object",
        "properties": {
          "notificationTargetId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "example": "Ops on-call email"
          },
          "channel": {
            "type": "string",
            "example": "EMAIL"
          },
          "delivered": {
            "type": "boolean",
            "description": "Whether the transport accepted the test notification"
          },
          "error": {
            "type": "string",
            "description": "Failure detail when delivered is false; empty otherwise.",
            "example": ""
          }
        },
        "required": [
          "notificationTargetId",
          "name",
          "channel",
          "delivered",
          "error"
        ]
      },
      "TestFireResponseDto": {
        "type": "object",
        "properties": {
          "ruleId": {
            "type": "string",
            "format": "uuid"
          },
          "matchedDeviceCount": {
            "type": "number",
            "description": "Number of devices the rule’s scope currently resolves to.",
            "example": 12
          },
          "condition": {
            "type": "string",
            "description": "Human-readable rendering of the condition, as the evaluator sees it.",
            "example": "cpu > 80 for 300s"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TestFireTargetResultDto"
            }
          }
        },
        "required": [
          "ruleId",
          "matchedDeviceCount",
          "condition",
          "results"
        ]
      },
      "NotificationTargetResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "example": "Ops on-call email"
          },
          "channel": {
            "type": "string",
            "enum": [
              "EMAIL",
              "WEBHOOK"
            ]
          },
          "email": {
            "type": "object",
            "description": "EMAIL targets only",
            "nullable": true
          },
          "url": {
            "type": "object",
            "description": "WEBHOOK targets only",
            "nullable": true
          },
          "headers": {
            "type": "object",
            "description": "WEBHOOK targets only. Header names with a masked value (********): the values are write-only because they often carry credentials (FM-1660).",
            "additionalProperties": {
              "type": "string"
            }
          },
          "signed": {
            "type": "boolean",
            "description": "Whether webhook deliveries to this target are signed"
          },
          "enabled": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "channel",
          "signed",
          "enabled",
          "createdAt",
          "updatedAt"
        ]
      },
      "CreateNotificationTargetDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable target name",
            "example": "Ops on-call email"
          },
          "channel": {
            "type": "string",
            "description": "Delivery transport",
            "enum": [
              "EMAIL",
              "WEBHOOK"
            ]
          },
          "email": {
            "type": "string",
            "description": "Recipient address. Required when channel is EMAIL.",
            "example": "ops@example.com"
          },
          "url": {
            "type": "string",
            "description": "Endpoint to POST to. Required when channel is WEBHOOK. Must be a public http(s) URL — private, loopback, link-local and in-cluster addresses are rejected to prevent SSRF.",
            "example": "https://hooks.example.com/services/T000/B000/XXXX"
          },
          "headers": {
            "type": "object",
            "description": "Extra headers sent on every webhook delivery. WEBHOOK only. Values are write-only: responses show the names with a masked value. At most 20; Host, Content-Length, Content-Type, Transfer-Encoding, Connection and the X-Webhook-* headers are reserved.",
            "additionalProperties": {
              "type": "string"
            },
            "example": {
              "X-Tenant-Token": "abc123"
            }
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the target receives deliveries",
            "default": true
          }
        },
        "required": [
          "name",
          "channel"
        ]
      },
      "CreatedNotificationTargetResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "example": "Ops on-call email"
          },
          "channel": {
            "type": "string",
            "enum": [
              "EMAIL",
              "WEBHOOK"
            ]
          },
          "email": {
            "type": "object",
            "description": "EMAIL targets only",
            "nullable": true
          },
          "url": {
            "type": "object",
            "description": "WEBHOOK targets only",
            "nullable": true
          },
          "headers": {
            "type": "object",
            "description": "WEBHOOK targets only. Header names with a masked value (********): the values are write-only because they often carry credentials (FM-1660).",
            "additionalProperties": {
              "type": "string"
            }
          },
          "signed": {
            "type": "boolean",
            "description": "Whether webhook deliveries to this target are signed"
          },
          "enabled": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "secret": {
            "type": "object",
            "description": "HMAC-SHA256 signing key for webhook deliveries. Shown once, on creation, and never returned again. Store it in the receiving system to verify X-Webhook-Signature.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "name",
          "channel",
          "signed",
          "enabled",
          "createdAt",
          "updatedAt"
        ]
      },
      "UpdateNotificationTargetDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable target name",
            "example": "Ops on-call email"
          },
          "email": {
            "type": "string",
            "description": "Recipient address. Required when channel is EMAIL.",
            "example": "ops@example.com"
          },
          "url": {
            "type": "string",
            "description": "Endpoint to POST to. Required when channel is WEBHOOK. Must be a public http(s) URL — private, loopback, link-local and in-cluster addresses are rejected to prevent SSRF.",
            "example": "https://hooks.example.com/services/T000/B000/XXXX"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the target receives deliveries",
            "default": true
          },
          "headers": {
            "type": "object",
            "description": "Replaces the custom headers. A string sets the value, null keeps the stored value of that name, and a name left out is removed. WEBHOOK only.",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            },
            "example": {
              "Authorization": null,
              "X-Tenant-Token": "new-value"
            }
          }
        }
      },
      "RotatedNotificationTargetSecretDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "secret": {
            "type": "string",
            "description": "The new HMAC-SHA256 signing key. Shown once and never returned again; the previous key stops working immediately."
          }
        },
        "required": [
          "id",
          "secret"
        ]
      }
    }
  }
}
