{
  "components": {
    "schemas": {
      "ActionKind": {
        "description": "v0.05 atomic step types for agent workflow execution.\n\nMirrors ``kyvvu_engine.schemas.ActionKind``.\n\nAttributes:\n    STEP_MODEL: LLM / model invocation.\n    STEP_RESOURCE: External resource access (API, DB, file).\n    STEP_MESSAGE: Outbound message to a human or system.\n    STEP_CREDENTIAL: Credential / secret access.\n    STEP_SELF: Agent-to-agent delegation.\n    STEP_EXEC: Code or shell execution.\n    STEP_GATE: Human-in-the-loop checkpoint.\n    STEP_UNKNOWN: Uncategorised step.\n    TASK_START: Task lifecycle \u2014 start.\n    TASK_END: Task lifecycle \u2014 end.\n    TASK_ERROR: Task lifecycle \u2014 error / abort.\n    TASK_IDLE: Task lifecycle \u2014 pause / waiting.",
        "enum": [
          "step.model",
          "step.resource",
          "step.message",
          "step.credential",
          "step.self",
          "step.exec",
          "step.gate",
          "step.unknown",
          "task.start",
          "task.end",
          "task.error",
          "task.idle"
        ],
        "title": "ActionKind",
        "type": "string"
      },
      "AgentApiKeyRebind": {
        "description": "Replacement agent API key chosen by a workspace administrator.",
        "properties": {
          "api_key_id": {
            "title": "Api Key Id",
            "type": "integer"
          }
        },
        "required": [
          "api_key_id"
        ],
        "title": "AgentApiKeyRebind",
        "type": "object"
      },
      "AgentCreate": {
        "description": "Schema for agent registration request.\n\nAttributes:\n    agent_key: Unique key for the agent.\n    name: Human-readable agent name.\n    purpose: Documented purpose of the agent.\n    owner_id: Email of the verified org member accountable for this agent. Must be\n        the email of a verified member of the organisation that owns the API key.\n        Auto-populated from the API key owner's email if omitted.\n    risk_classification: Risk level (limited, high, etc).\n    maintainer_id: Free-text identifier for the person or team that maintains this\n        agent (e.g. an email, team name, or Slack handle). Not validated against\n        platform users \u2014 any value is accepted. Auto-populated from the API key\n        owner's email if omitted.\n    metadata: Additional metadata.\n    environment: Deployment environment (development, qa, staging, production).\n\nNote:\n    owner_id carries EU AI Act accountability and must be a verified org member.\n    maintainer_id is informational free-text and accepts any value.",
        "example": {
          "agent_key": "customer-support-bot",
          "environment": "production",
          "name": "Customer Support Agent",
          "purpose": "Handles customer inquiries and support tickets",
          "risk_classification": "high"
        },
        "properties": {
          "agent_key": {
            "description": "Unique stable identifier for this agent",
            "maxLength": 255,
            "minLength": 1,
            "title": "Agent Key",
            "type": "string"
          },
          "declared_tools": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "List of tool/step names this agent is allowed to invoke (least-agency enforcement)",
            "title": "Declared Tools"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Free-text description of the agent. Only persisted when store_metadata=True.",
            "title": "Description"
          },
          "environment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": "development",
            "description": "Deployment environment (development, qa, staging, production)",
            "title": "Environment"
          },
          "maintainer_id": {
            "anyOf": [
              {
                "maxLength": 255,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Free-text identifier for the person or team maintaining this agent (e.g. an email, team name, or Slack handle). Not validated \u2014 any value accepted. Only persisted when store_metadata=True.",
            "title": "Maintainer Id"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Additional custom metadata",
            "title": "Metadata"
          },
          "name": {
            "anyOf": [
              {
                "maxLength": 255,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Human-readable agent name",
            "title": "Name"
          },
          "owner_id": {
            "anyOf": [
              {
                "maxLength": 255,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Email of the verified org member accountable for this agent (EU AI Act Article 16). Must be the email of a verified member of the organisation that owns the API key. Only persisted when store_metadata=True.",
            "title": "Owner Id"
          },
          "purpose": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Documented purpose of the agent (EU AI Act Article 13 requirement)",
            "title": "Purpose"
          },
          "risk_classification": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": "unclassified",
            "description": "EU AI Act risk classification (high, limited, minimal, unacceptable, unclassified)",
            "title": "Risk Classification"
          },
          "store_metadata": {
            "default": false,
            "description": "When True, name/purpose/owner_id/maintainer_id/description/metadata are persisted in Kyvvu's cloud. When False (default), these fields are not stored server-side.",
            "title": "Store Metadata",
            "type": "boolean"
          }
        },
        "required": [
          "agent_key"
        ],
        "title": "AgentCreate",
        "type": "object"
      },
      "AgentListResponse": {
        "description": "Agent details plus the latest policy fetch for the Agents page.",
        "properties": {
          "active": {
            "default": true,
            "title": "Active",
            "type": "boolean"
          },
          "agent_id": {
            "title": "Agent Id",
            "type": "string"
          },
          "agent_key": {
            "title": "Agent Key",
            "type": "string"
          },
          "client": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ClientVersionResponse"
              },
              {
                "type": "null"
              }
            ],
            "description": "The agent's client versions, or ``None`` if never recorded.",
            "readOnly": true
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "declared_tools": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Declared Tools"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "environment": {
            "title": "Environment",
            "type": "string"
          },
          "expected_cadence_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expected Cadence Seconds"
          },
          "last_fetch": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Fetch"
          },
          "last_seen_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Seen At"
          },
          "maintainer_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Maintainer Id"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Metadata"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "owner_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Owner Id"
          },
          "purpose": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Purpose"
          },
          "risk_classification": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Risk Classification"
          },
          "silent": {
            "default": false,
            "title": "Silent",
            "type": "boolean"
          },
          "store_metadata": {
            "default": false,
            "title": "Store Metadata",
            "type": "boolean"
          },
          "updated_at": {
            "format": "date-time",
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "agent_id",
          "agent_key",
          "environment",
          "created_at",
          "updated_at",
          "client"
        ],
        "title": "AgentListResponse",
        "type": "object"
      },
      "AgentRegistrationResponse": {
        "description": "Schema for agent registration response.\n\nExtends AgentResponse with registration status to indicate\nwhether this was a new registration or an update to existing agent.\n\nAttributes:\n    registration_status: Either 'created' or 'updated'.\n    incidents: List of incidents detected during registration.\n    baseline: Outcome of applying the bundled baseline manifests.\n        Registration never fails because the baseline could not be\n        applied, so this is the only place the fact leaves the server.\n    All other fields inherited from AgentResponse.",
        "properties": {
          "active": {
            "default": true,
            "title": "Active",
            "type": "boolean"
          },
          "agent_id": {
            "title": "Agent Id",
            "type": "string"
          },
          "agent_key": {
            "title": "Agent Key",
            "type": "string"
          },
          "baseline": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BaselineStatusResponse"
              },
              {
                "type": "null"
              }
            ],
            "description": "Whether Kyvvu's built-in baseline is actually enforcing on this agent. `status` is complete / partial / failed; compare `policy_count` with `expected_policy_count`."
          },
          "client": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ClientVersionResponse"
              },
              {
                "type": "null"
              }
            ],
            "description": "The agent's client versions, or ``None`` if never recorded.",
            "readOnly": true
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "declared_tools": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Declared Tools"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "environment": {
            "title": "Environment",
            "type": "string"
          },
          "expected_cadence_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expected Cadence Seconds"
          },
          "incidents": {
            "description": "Policy incidents detected during registration (empty when compliant)",
            "items": {},
            "title": "Incidents",
            "type": "array"
          },
          "last_seen_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Seen At"
          },
          "maintainer_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Maintainer Id"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Metadata"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "owner_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Owner Id"
          },
          "purpose": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Purpose"
          },
          "registration_status": {
            "description": "Registration status: 'created' for new agents, 'updated' for existing",
            "title": "Registration Status",
            "type": "string"
          },
          "risk_classification": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Risk Classification"
          },
          "silent": {
            "default": false,
            "title": "Silent",
            "type": "boolean"
          },
          "store_metadata": {
            "default": false,
            "title": "Store Metadata",
            "type": "boolean"
          },
          "updated_at": {
            "format": "date-time",
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "agent_id",
          "agent_key",
          "environment",
          "created_at",
          "updated_at",
          "registration_status",
          "client"
        ],
        "title": "AgentRegistrationResponse",
        "type": "object"
      },
      "AgentResponse": {
        "description": "Schema for agent response.\n\nAttributes:\n    agent_id: Unique agent identifier.\n    agent_key: Agent key.\n    name: Human-readable name.\n    purpose: Documented purpose.\n    owner_id: Owner identifier.\n    risk_classification: Risk level.\n    maintainer_id: Maintainer identifier.\n    metadata: Additional metadata.\n    environment: Deployment environment.\n    active: Whether agent is active (False if deactivated).\n    created_at: Creation timestamp.\n    updated_at: Last update timestamp.",
        "properties": {
          "active": {
            "default": true,
            "title": "Active",
            "type": "boolean"
          },
          "agent_id": {
            "title": "Agent Id",
            "type": "string"
          },
          "agent_key": {
            "title": "Agent Key",
            "type": "string"
          },
          "client": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ClientVersionResponse"
              },
              {
                "type": "null"
              }
            ],
            "description": "The agent's client versions, or ``None`` if never recorded.",
            "readOnly": true
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "declared_tools": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Declared Tools"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "environment": {
            "title": "Environment",
            "type": "string"
          },
          "expected_cadence_seconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expected Cadence Seconds"
          },
          "last_seen_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Seen At"
          },
          "maintainer_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Maintainer Id"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Metadata"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "owner_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Owner Id"
          },
          "purpose": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Purpose"
          },
          "risk_classification": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Risk Classification"
          },
          "silent": {
            "default": false,
            "title": "Silent",
            "type": "boolean"
          },
          "store_metadata": {
            "default": false,
            "title": "Store Metadata",
            "type": "boolean"
          },
          "updated_at": {
            "format": "date-time",
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "agent_id",
          "agent_key",
          "environment",
          "created_at",
          "updated_at",
          "client"
        ],
        "title": "AgentResponse",
        "type": "object"
      },
      "AssignmentCreate": {
        "description": "Schema for creating a new manifest assignment.\n\nAttributes:\n    agent_id: Agent identifier (SHA-256 hash string).\n    repo_id: Integer PK of the repository containing the manifest. Omit it\n        to restore a bundled baseline manifest: ``manifest_path``\n        is then the path ``GET /api/v1/baseline`` reports for it, and any\n        other path is rejected with 422.\n    manifest_path: File path in the repo (e.g. \"owasp_agentic_default.yaml\"),\n        or, with no ``repo_id``, a bundled baseline manifest's path.",
        "example": {
          "agent_id": "agent_abc123def456",
          "manifest_path": "owasp_agentic_default.yaml",
          "repo_id": 1
        },
        "properties": {
          "agent_id": {
            "description": "Agent identifier (agent_id hash)",
            "minLength": 1,
            "title": "Agent Id",
            "type": "string"
          },
          "manifest_path": {
            "description": "YAML file path in repo, or a bundled baseline manifest's path",
            "minLength": 1,
            "title": "Manifest Path",
            "type": "string"
          },
          "repo_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Repository integer PK. Omit to assign a bundled baseline manifest by its bundled path (restores one that was unassigned).",
            "title": "Repo Id"
          }
        },
        "required": [
          "agent_id",
          "manifest_path"
        ],
        "title": "AssignmentCreate",
        "type": "object"
      },
      "AssignmentResponse": {
        "description": "Schema for assignment list/create responses.\n\nAttributes:\n    id: Assignment primary key.\n    agent_id: Agent identifier (SHA-256 hash string).\n    repo_id: Repository integer PK, or null for a bundled baseline\n        manifest, which came from the platform and not from a repo.\n    manifest_path: YAML file path in repo. For a baseline manifest, the\n        path the bundled copy was taken from.\n    manifest_sha: Git blob SHA (pinned version). For a baseline manifest,\n        the SHA-256 of the bundled file's bytes.\n    manifest_ref: Git branch/tag at assignment time. For a baseline\n        manifest, the manifest's own declared version.\n    manifest_name: Human-readable manifest name.\n    source: Where the manifest came from -- ``\"repo\"`` or ``\"baseline\"``.\n        Derived, so a client does not have to infer it from a null\n        ``repo_id``.\n    manifest_version: The manifest version this assignment pins, for a\n        baseline manifest. Null for a repo manifest, whose version is\n        whatever the branch in ``manifest_ref`` held at ``manifest_sha``.\n    policy_count: Number of policies created from this manifest.\n    created_at: When the assignment was created.\n    updated_at: When the assignment was last modified.",
        "properties": {
          "agent_id": {
            "title": "Agent Id",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "integer"
          },
          "manifest_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Name"
          },
          "manifest_path": {
            "title": "Manifest Path",
            "type": "string"
          },
          "manifest_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Ref"
          },
          "manifest_sha": {
            "title": "Manifest Sha",
            "type": "string"
          },
          "manifest_version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Version"
          },
          "policy_count": {
            "default": 0,
            "title": "Policy Count",
            "type": "integer"
          },
          "repo_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Repo Id"
          },
          "source": {
            "default": "repo",
            "enum": [
              "repo",
              "baseline"
            ],
            "title": "Source",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "title": "Updated At",
            "type": "string"
          },
          "warnings": {
            "items": {
              "type": "string"
            },
            "title": "Warnings",
            "type": "array"
          }
        },
        "required": [
          "id",
          "agent_id",
          "manifest_path",
          "manifest_sha",
          "created_at",
          "updated_at"
        ],
        "title": "AssignmentResponse",
        "type": "object"
      },
      "AuthMeResponse": {
        "description": "Schema for GET /auth/me.\n\nMinimal identity payload for the one endpoint an account that cannot act\nanywhere else can still call (gated by ``require_user_credential``, which\nchecks ``is_active`` and nothing about admission). The dashboard reads it\nto decide which waiting screen to show.\n\n**``is_verified`` was published here and is gone**, replaced by the two\nthings it conflated. That makes this an API contract change rather than an\ninternal one, which is why the release gate binds packages 7, 8 and 11.\nThe third thing it meant -- \"an administrator has not approved you\" --\nhas no replacement, because nobody arrives unapproved any more.\n\nAttributes:\n    email: User's email address.\n    org_id: The workspace this session is acting as.\n    is_capped: Whether the ACCOUNT arrived after the registration cap and\n        is waiting to be released by Kyvvu staff. True in every workspace\n        it holds, because it is about the account.\n    is_suspended: Whether this account's membership of the CURRENT\n        workspace is suspended. About one person in one organization, so\n        it can differ between workspaces.\n    name: User's display name.",
        "properties": {
          "email": {
            "description": "User email address",
            "format": "email",
            "title": "Email",
            "type": "string"
          },
          "is_capped": {
            "description": "Waiting to be released by Kyvvu staff",
            "title": "Is Capped",
            "type": "boolean"
          },
          "is_suspended": {
            "description": "Suspended in the current workspace",
            "title": "Is Suspended",
            "type": "boolean"
          },
          "name": {
            "description": "User display name",
            "title": "Name",
            "type": "string"
          },
          "org_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "The workspace this session acts as",
            "title": "Org Id"
          }
        },
        "required": [
          "email",
          "is_capped",
          "is_suspended",
          "name"
        ],
        "title": "AuthMeResponse",
        "type": "object"
      },
      "BaselineManifestStatusResponse": {
        "description": "What happened to one bundled baseline manifest at registration.\n\nAttributes:\n    manifest_path: The bundled manifest's path, which is also the\n        ``manifest_path`` of the assignment it produced.\n    manifest_name: Its human-readable name.\n    manifest_version: Its declared version.\n    status: ``applied`` / ``already_present`` / ``unassigned`` / ``failed``.\n    policy_count: How many of its policies are enabled on the agent now.\n    error: Exception class name, when the manifest could not be applied.",
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "manifest_name": {
            "title": "Manifest Name",
            "type": "string"
          },
          "manifest_path": {
            "title": "Manifest Path",
            "type": "string"
          },
          "manifest_version": {
            "title": "Manifest Version",
            "type": "string"
          },
          "policy_count": {
            "title": "Policy Count",
            "type": "integer"
          },
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "manifest_path",
          "manifest_name",
          "manifest_version",
          "status",
          "policy_count"
        ],
        "title": "BaselineManifestStatusResponse",
        "type": "object"
      },
      "BaselineStatusResponse": {
        "description": "Whether the agent is actually governed by the baseline.\n\nRegistration does not fail when the baseline cannot be applied -- an\nunusable agent is a worse outcome -- so the fact has to leave the server\nsome other way. This is that way, and it is the only one: a *partial*\nbaseline is indistinguishable from a healthy one everywhere else. Losing\nonly the security manifest leaves the seven compliance policies enforcing,\nso the engine reports ``enforcing`` with nothing wrong, the step that\nshould have been blocked runs, and the \"UNGOVERNED\" signal never fires,\nbecause nothing failed to *load*.\n\nAttributes:\n    status: ``complete`` (nothing was lost; an owner-removed manifest still\n        counts), ``partial`` (something the platform meant to enforce is\n        not enforcing), or ``failed`` (no bundled manifest is in effect).\n    policy_count: Baseline policies enabled on this agent now.\n    expected_policy_count: How many the bundle holds.\n    manifests: One entry per bundled manifest, in load order.",
        "properties": {
          "expected_policy_count": {
            "title": "Expected Policy Count",
            "type": "integer"
          },
          "manifests": {
            "items": {
              "$ref": "#/components/schemas/BaselineManifestStatusResponse"
            },
            "title": "Manifests",
            "type": "array"
          },
          "policy_count": {
            "title": "Policy Count",
            "type": "integer"
          },
          "status": {
            "description": "complete / partial / failed",
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status",
          "policy_count",
          "expected_policy_count"
        ],
        "title": "BaselineStatusResponse",
        "type": "object"
      },
      "BatchLogCreate": {
        "additionalProperties": false,
        "description": "Request body for POST /api/v1/logs/batch.\n\nAttributes:\n    agent_id: Registered agent identifier.\n    task_id: Task identifier (groups all steps).\n    steps: Ordered list of behavior steps for this task.",
        "properties": {
          "agent_id": {
            "description": "Agent ID from registration",
            "maxLength": 255,
            "title": "Agent Id",
            "type": "string"
          },
          "steps": {
            "description": "Ordered steps (1..1000)",
            "items": {
              "$ref": "#/components/schemas/BatchStep"
            },
            "maxItems": 1000,
            "minItems": 1,
            "title": "Steps",
            "type": "array"
          },
          "task_id": {
            "description": "Task identifier",
            "maxLength": 100,
            "title": "Task Id",
            "type": "string"
          }
        },
        "required": [
          "agent_id",
          "task_id",
          "steps"
        ],
        "title": "BatchLogCreate",
        "type": "object"
      },
      "BatchLogResponse": {
        "description": "Response from POST /api/v1/logs/batch.\n\nAttributes:\n    task_id: Task identifier.\n    steps_logged: Number of steps stored (the 201 is sent after the commit).\n    hash_tail: Hash of the last stored entry of the v1 chain. A client\n        that computed the chain (``hash`` on each step) compares it with\n        its own chain head. It is not a signed receipt.",
        "properties": {
          "hash_tail": {
            "description": "Hash of the last stored entry of the v1 chain. Equals the client's own chain head when the client computed the chain. Not a signed receipt.",
            "title": "Hash Tail",
            "type": "string"
          },
          "steps_logged": {
            "title": "Steps Logged",
            "type": "integer"
          },
          "task_id": {
            "title": "Task Id",
            "type": "string"
          }
        },
        "required": [
          "task_id",
          "steps_logged",
          "hash_tail"
        ],
        "title": "BatchLogResponse",
        "type": "object"
      },
      "BatchStep": {
        "additionalProperties": false,
        "description": "A single v0.05 behavior step for batch log ingestion.\n\nAttributes:\n    seq: The producer's per-task ordinal for this step. In a batch that\n        carries ``hash`` it is authoritative and required: 1, 2, 3, ...\n        per (agent, task) across all of the task's batches, so a batch's\n        first ``seq`` must be the stored tail + 1. In a batch without\n        ``hash`` the server assigns ``seq`` and ignores this value. A step\n        that carries the retired ``step`` key is refused (422).\n    hash: The v1 chain hash of this step, computed by the client\n        (``kyvvu_engine.chain_entry_hash``). The server recomputes\n        it and refuses the batch (422 ``hash_mismatch``) on any\n        difference. Either every step of a batch carries one or none does;\n        without it the server computes the chain itself.\n    parent_task_id: Parent task identifier on ``task.start`` only.\n    kind: The action kind (e.g. ``step.model``, ``step.resource``). A step that\n        carries the retired ``step_type`` key is refused (422).\n    verb: HTTP-style verb (``GET``, ``POST``, \u2026) where applicable.\n    step_name: Human-readable step name (typically the decorated function name).\n    properties: Structured properties for this step type (model, target, \u2026).\n    input: Function input arguments.\n    output: Function return value.\n    timestamp: RFC 3339 timestamp with an offset, from the agent.",
        "properties": {
          "aag_version": {
            "anyOf": [
              {
                "maxLength": 255,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "AAG conformance declaration",
            "title": "Aag Version"
          },
          "action_id": {
            "anyOf": [
              {
                "maxLength": 255,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Action identifier",
            "title": "Action Id"
          },
          "hash": {
            "anyOf": [
              {
                "pattern": "^[0-9a-f]{64}$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Client-computed v1 chain hash of this step (lowercase hex SHA-256). Verified by the server; omit it on every step to have the server compute the chain.",
            "title": "Hash"
          },
          "input": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Input arguments",
            "title": "Input"
          },
          "kind": {
            "$ref": "#/components/schemas/ActionKind",
            "description": "Action kind (e.g. 'step.model', 'task.start')"
          },
          "meta": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Arbitrary step metadata",
            "title": "Meta"
          },
          "output": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Return value",
            "title": "Output"
          },
          "parent_task_id": {
            "anyOf": [
              {
                "maxLength": 100,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Parent task identifier",
            "title": "Parent Task Id"
          },
          "properties": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Step properties",
            "title": "Properties"
          },
          "seq": {
            "anyOf": [
              {
                "maximum": 2147483647.0,
                "minimum": 0.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Producer's per-task ordinal. Authoritative (and required) when the batch carries hash: tail+1, tail+2, ... Ignored otherwise (the server assigns seq).",
            "title": "Seq"
          },
          "step_name": {
            "description": "Step / function name",
            "maxLength": 255,
            "title": "Step Name",
            "type": "string"
          },
          "timestamp": {
            "description": "RFC 3339 timestamp with offset, at most 6 fractional digits",
            "format": "date-time",
            "title": "Timestamp",
            "type": "string"
          },
          "verb": {
            "anyOf": [
              {
                "maxLength": 10,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "HTTP verb (GET/POST/\u2026)",
            "title": "Verb"
          }
        },
        "required": [
          "kind",
          "step_name",
          "timestamp"
        ],
        "title": "BatchStep",
        "type": "object"
      },
      "ClientVersionResponse": {
        "description": "What an agent's client last announced, and whether it needs updating.\n\nAttributes:\n    name: The ``X-Kyvvu-Client`` value, such as ``kyvvu-claude/0.7.0``;\n        ``legacy`` for a policy fetch that announced nothing (an engine\n        too old to), ``rest`` for a registration or batch that did.\n    engine_version: The kyvvu-engine version it runs.\n    sdk_version: The `kyvvu` Python client version, when it uses that client.\n    policy_schema: The policy schema it can load.\n    legacy: Whether it is an engine that predates the version headers.\n    needs_update: Whether it is below the platform's floor, or legacy.\n        Never for ``rest`` or a client named without an engine version.\n    needs_update_by: The date of an announced upcoming floor this client\n        is below; ``None`` when none is announced or it meets it.\n    seen_at: When these values were last recorded as changed.\n    refused_at: When the platform last refused it as too old; ``None``\n        once a later request was accepted.",
        "properties": {
          "engine_version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Engine Version"
          },
          "legacy": {
            "default": false,
            "title": "Legacy",
            "type": "boolean"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "needs_update": {
            "default": false,
            "title": "Needs Update",
            "type": "boolean"
          },
          "needs_update_by": {
            "anyOf": [
              {
                "format": "date",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Needs Update By"
          },
          "policy_schema": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Policy Schema"
          },
          "refused_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Refused At"
          },
          "sdk_version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sdk Version"
          },
          "seen_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Seen At"
          }
        },
        "required": [
          "name"
        ],
        "title": "ClientVersionResponse",
        "type": "object"
      },
      "DeviceCodeStartRequest": {
        "description": "Schema for starting the CLI device-code authorization flow.\n\nAttributes:\n    device_label: Human-readable label for the requesting device (e.g.\n        hostname), supplied by the CLI. Optional -- defaults to a\n        generic ``\"CLI\"`` label if omitted.",
        "properties": {
          "device_label": {
            "anyOf": [
              {
                "maxLength": 255,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Human-readable label for the requesting device (e.g. hostname)",
            "title": "Device Label"
          }
        },
        "title": "DeviceCodeStartRequest",
        "type": "object"
      },
      "DeviceCodeStartResponse": {
        "description": "Schema for the CLI device-code authorization start response.\n\nMirrors the RFC 8628 Device Authorization Grant response shape.\n\nAttributes:\n    device_code: Long, high-entropy secret the CLI polls\n        ``POST /auth/cli/token`` with. Shown to the CLI once; never\n        persisted in plaintext.\n    user_code: Short code the CLI displays as a terminal fallback and\n        embeds in ``verification_uri_complete``.\n    verification_uri: Browser URL where the user enters ``user_code``\n        (used as the base for ``verification_uri_complete`` below; not\n        opened directly by the CLI).\n    verification_uri_complete: Convenience URL carrying ``user_code`` as\n        a query parameter, per RFC 8628. The CLI opens this URL\n        automatically, and the browser page (cli-auth.html) pre-fills\n        its code input from it -- same pattern as GitHub's/Google's own\n        device-authorization flows. **Security note:** pre-filling is\n        not the security boundary here -- the page never auto-submits on\n        load, so approval still requires an explicit click from an\n        authenticated human, and ``/auth/cli/approve``'s per-IP rate\n        limit remains the defense against guessing ``user_code``. See\n        the CLI device-code login flow (D1) comment in ``router.py`` for\n        the full threat-model discussion, including what this design\n        does *not* defend against (a phishing link carrying a code the\n        attacker already knows).\n    expires_in: Seconds until ``device_code``/``user_code`` expire.\n    interval: Minimum seconds the CLI should wait between polls of\n        ``POST /auth/cli/token``.",
        "properties": {
          "device_code": {
            "description": "Long device-side secret; poll /auth/cli/token with this",
            "title": "Device Code",
            "type": "string"
          },
          "expires_in": {
            "description": "Seconds until device_code/user_code expire",
            "title": "Expires In",
            "type": "integer"
          },
          "interval": {
            "description": "Minimum seconds between /auth/cli/token polls",
            "title": "Interval",
            "type": "integer"
          },
          "user_code": {
            "description": "Short human-typed code to enter at verification_uri",
            "title": "User Code",
            "type": "string"
          },
          "verification_uri": {
            "description": "Browser URL where the user enters user_code",
            "title": "Verification Uri",
            "type": "string"
          },
          "verification_uri_complete": {
            "description": "Convenience URL carrying user_code as a query parameter (RFC 8628). The CLI opens this automatically and the browser page pre-fills its code input from it -- see this schema's docstring.",
            "title": "Verification Uri Complete",
            "type": "string"
          }
        },
        "required": [
          "device_code",
          "user_code",
          "verification_uri",
          "verification_uri_complete",
          "expires_in",
          "interval"
        ],
        "title": "DeviceCodeStartResponse",
        "type": "object"
      },
      "DeviceCodeTokenRequest": {
        "description": "Schema for the CLI device-code token exchange request.\n\nAttributes:\n    device_code: The long, raw device-side secret returned by\n        ``POST /auth/cli/start``.",
        "properties": {
          "device_code": {
            "description": "Raw device_code from /auth/cli/start",
            "title": "Device Code",
            "type": "string"
          }
        },
        "required": [
          "device_code"
        ],
        "title": "DeviceCodeTokenRequest",
        "type": "object"
      },
      "EngineIncidentCreate": {
        "description": "Payload POSTed by the kyvvu-engine to ``POST /api/v1/incidents``.\n\nThe engine sends this after evaluating policies in-process and detecting\none or more violations. Authentication uses the agent's ``Bearer KvKey-...``\nAPI key, which identifies the agent without a separate ``agent_id`` lookup.\n\nAttributes:\n    agent_id: Agent hash-string identifier (e.g. ``agent_abc123``).\n    enforcement_point: Policy enforcement point that was evaluated\n        (``agent_registration`` or ``step_execution``).\n    action: Aggregate engine action (``warn`` or ``block``).\n    risk_score: Aggregate risk score (0.0 \u2013 1.0).\n    violations: List of individual policy violations.\n    task_id: Task identifier for step-level and task-level incidents.\n        ``None`` for agent_registration incidents.\n    step_name: Human-readable step name for step-level incidents.\n    kind: Action kind string (e.g. ``step.model``).\n    timestamp: ISO 8601 timestamp from the engine.",
        "properties": {
          "action": {
            "description": "Aggregate action: warn or block",
            "title": "Action",
            "type": "string"
          },
          "action_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Action the violation was found on; links the incident to its log entry",
            "title": "Action Id"
          },
          "agent_id": {
            "description": "Agent hash-string identifier",
            "title": "Agent Id",
            "type": "string"
          },
          "enforcement_point": {
            "description": "Policy enforcement point (agent_registration / step_execution)",
            "title": "Enforcement Point",
            "type": "string"
          },
          "kind": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ActionKind"
              },
              {
                "type": "null"
              }
            ],
            "description": "Action kind"
          },
          "risk_score": {
            "description": "Aggregate risk score 0.0\u20131.0",
            "title": "Risk Score",
            "type": "number"
          },
          "step_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Step name for step incidents",
            "title": "Step Name"
          },
          "task_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Task ID for step/task incidents",
            "title": "Task Id"
          },
          "timestamp": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 timestamp from engine",
            "title": "Timestamp"
          },
          "violations": {
            "description": "Individual policy violations",
            "items": {
              "$ref": "#/components/schemas/EngineViolation"
            },
            "title": "Violations",
            "type": "array"
          }
        },
        "required": [
          "agent_id",
          "enforcement_point",
          "action",
          "risk_score",
          "violations"
        ],
        "title": "EngineIncidentCreate",
        "type": "object"
      },
      "EngineIncidentResponse": {
        "description": "Response from ``POST /api/v1/incidents``.\n\nAttributes:\n    created: Number of new incident records created.\n    updated: Number of existing incident records updated (last_occurrence bumped).\n    incident_ids: List of incident_id strings for all affected records.",
        "properties": {
          "created": {
            "title": "Created",
            "type": "integer"
          },
          "incident_ids": {
            "items": {
              "type": "string"
            },
            "title": "Incident Ids",
            "type": "array"
          },
          "updated": {
            "title": "Updated",
            "type": "integer"
          }
        },
        "required": [
          "created",
          "updated",
          "incident_ids"
        ],
        "title": "EngineIncidentResponse",
        "type": "object"
      },
      "EngineViolation": {
        "description": "A single policy violation reported by the kyvvu-engine.\n\nAttributes:\n    policy_name: Human-readable policy name.\n    policy_id: Policy ID, when the policy came from the platform.\n    manifest_id: Assignment ID, when the policy came from a platform manifest.\n    manifest_name: Name of the source manifest, when present.\n    severity: Severity level as evaluated by the engine.\n    rule_type: Rule type evaluated by the engine, when supplied.\n    details: Optional structured violation context from the rule function.",
        "properties": {
          "details": {
            "anyOf": [
              {},
              {
                "type": "null"
              }
            ],
            "description": "Structured violation context",
            "title": "Details"
          },
          "manifest_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Id"
          },
          "manifest_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Name"
          },
          "policy_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Policy Id"
          },
          "policy_name": {
            "description": "Name of the violated policy",
            "title": "Policy Name",
            "type": "string"
          },
          "rule_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Rule type evaluated by the engine",
            "title": "Rule Type"
          },
          "severity": {
            "$ref": "#/components/schemas/Severity",
            "description": "Severity level (low/medium/high/critical)"
          }
        },
        "required": [
          "policy_name",
          "severity"
        ],
        "title": "EngineViolation",
        "type": "object"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "ManifestInfo": {
        "description": "Parsed and validated manifest metadata.\n\nAttributes:\n    name: Manifest name.\n    description: Manifest description.\n    version: Manifest version.\n    policy_count: Number of policies.\n    policies: Parsed policy entries.\n    valid: Whether the manifest passed validation.\n    validation_errors: List of validation errors.\n    validation_warnings: List of validation warnings.",
        "properties": {
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "policies": {
            "items": {
              "$ref": "#/components/schemas/ManifestPolicyInfo"
            },
            "title": "Policies",
            "type": "array"
          },
          "policy_count": {
            "default": 0,
            "title": "Policy Count",
            "type": "integer"
          },
          "valid": {
            "default": true,
            "title": "Valid",
            "type": "boolean"
          },
          "validation_errors": {
            "items": {
              "type": "string"
            },
            "title": "Validation Errors",
            "type": "array"
          },
          "validation_warnings": {
            "items": {
              "type": "string"
            },
            "title": "Validation Warnings",
            "type": "array"
          },
          "version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Version"
          }
        },
        "title": "ManifestInfo",
        "type": "object"
      },
      "ManifestPolicyInfo": {
        "description": "A single policy entry parsed from a manifest.\n\nAttributes:\n    name: Policy name.\n    rule_type: Rule function name.\n    severity: Severity level.\n    effect: Action if this policy is violated under the default aggregator.\n    enforcement_point: When the policy is evaluated.\n    description: Optional description.\n    params: Optional rule parameters.\n    risk_classification: Optional risk classification.",
        "properties": {
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "effect": {
            "description": "Action for a violation under the default aggregator.",
            "readOnly": true,
            "title": "Effect",
            "type": "string"
          },
          "enforcement_point": {
            "title": "Enforcement Point",
            "type": "string"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "params": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Params"
          },
          "risk_classification": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Risk Classification"
          },
          "rule_type": {
            "title": "Rule Type",
            "type": "string"
          },
          "severity": {
            "title": "Severity",
            "type": "string"
          }
        },
        "required": [
          "name",
          "rule_type",
          "severity",
          "enforcement_point",
          "effect"
        ],
        "title": "ManifestPolicyInfo",
        "type": "object"
      },
      "PolicyResponse": {
        "description": "Schema for policy response.\n\nAttributes:\n    id: Policy ID.\n    name: Policy name.\n    description: Policy description.\n    enforcement_point: When policy applies.\n    agent_id: Specific agent or null for all.\n    risk_classification: Risk level filter (high, limited, minimal, null = all agents).\n    rule_type: Type of rule.\n    params: Rule parameters.\n    severity: Impact level.\n    effect: Action if this policy is violated under the default aggregator.\n    enabled: Whether policy is active.\n    created_at: Creation timestamp.\n    updated_at: Last update timestamp.\n    source_template: Template used to create this policy (if any).\n    evaluate_at: Reserved for future use (currently NULL).\n    manifest_id: Assignment row id when this policy was materialised from a\n        manifest assignment; ``None`` for hand-authored policies.\n    manifest_name: Human-readable manifest name when sourced from an\n        assignment.\n    manifest_repo: Source repository (``owner/name``) of the manifest.\n    manifest_path: File path of the manifest within the repo.\n    manifest_sha: Git blob SHA pinned at assignment time.\n    manifest_assigned_at: ISO-8601 timestamp of the assignment.\n    manifest_assigned_by: User id who created the assignment.",
        "properties": {
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Id"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "effect": {
            "description": "Action for a violation under the default aggregator.",
            "readOnly": true,
            "title": "Effect",
            "type": "string"
          },
          "enabled": {
            "title": "Enabled",
            "type": "boolean"
          },
          "enforcement_point": {
            "title": "Enforcement Point",
            "type": "string"
          },
          "environment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Environment"
          },
          "evaluate_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Evaluate At"
          },
          "id": {
            "title": "Id",
            "type": "integer"
          },
          "manifest_assigned_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Assigned At"
          },
          "manifest_assigned_by": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Assigned By"
          },
          "manifest_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Id"
          },
          "manifest_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Name"
          },
          "manifest_path": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Path"
          },
          "manifest_repo": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Repo"
          },
          "manifest_sha": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Manifest Sha"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "params": {
            "additionalProperties": true,
            "title": "Params",
            "type": "object"
          },
          "risk_classification": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Risk Classification"
          },
          "rule_type": {
            "title": "Rule Type",
            "type": "string"
          },
          "severity": {
            "title": "Severity",
            "type": "string"
          },
          "source_template": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source Template"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "enforcement_point",
          "agent_id",
          "risk_classification",
          "rule_type",
          "params",
          "severity",
          "enabled",
          "created_at",
          "updated_at",
          "source_template",
          "evaluate_at",
          "effect"
        ],
        "title": "PolicyResponse",
        "type": "object"
      },
      "RepoFile": {
        "description": "A single YAML file entry from a GitHub repo tree.\n\nAttributes:\n    path: File path relative to the repo root.\n    sha: Git blob SHA.\n    size: File size in bytes.",
        "properties": {
          "path": {
            "title": "Path",
            "type": "string"
          },
          "sha": {
            "title": "Sha",
            "type": "string"
          },
          "size": {
            "title": "Size",
            "type": "integer"
          }
        },
        "required": [
          "path",
          "sha",
          "size"
        ],
        "title": "RepoFile",
        "type": "object"
      },
      "RepoFileContentResponse": {
        "description": "Response from fetching a single file's content and manifest validation.\n\nAttributes:\n    repo: Formatted as \"owner/name\".\n    branch: Branch the file was read from.\n    file_path: Path to the file.\n    sha: Git blob SHA.\n    content: Raw YAML string.\n    manifest: Parsed and validated manifest info.",
        "properties": {
          "branch": {
            "title": "Branch",
            "type": "string"
          },
          "content": {
            "title": "Content",
            "type": "string"
          },
          "file_path": {
            "title": "File Path",
            "type": "string"
          },
          "manifest": {
            "$ref": "#/components/schemas/ManifestInfo"
          },
          "repo": {
            "title": "Repo",
            "type": "string"
          },
          "sha": {
            "title": "Sha",
            "type": "string"
          }
        },
        "required": [
          "repo",
          "branch",
          "file_path",
          "sha",
          "content",
          "manifest"
        ],
        "title": "RepoFileContentResponse",
        "type": "object"
      },
      "RepoReadResponse": {
        "description": "Response from reading YAML files in a connected repository.\n\nAttributes:\n    repo: Formatted as \"owner/name\".\n    branch: The branch that was read.\n    files: List of YAML/YML files found.\n    truncated: True if the GitHub tree response was truncated.",
        "properties": {
          "branch": {
            "title": "Branch",
            "type": "string"
          },
          "files": {
            "items": {
              "$ref": "#/components/schemas/RepoFile"
            },
            "title": "Files",
            "type": "array"
          },
          "repo": {
            "title": "Repo",
            "type": "string"
          },
          "truncated": {
            "title": "Truncated",
            "type": "boolean"
          }
        },
        "required": [
          "repo",
          "branch",
          "files",
          "truncated"
        ],
        "title": "RepoReadResponse",
        "type": "object"
      },
      "RepoResponse": {
        "description": "Schema for repository responses. Never includes the token.\n\nAttributes:\n    id: Repository primary key.\n    owner: GitHub owner or organisation.\n    name: GitHub repository name.\n    branch: Git branch (always resolved \u2014 never empty).\n    path: Subdirectory scanning is scoped to, or null for the whole repo.\n    created_at: When the repo connection was registered.",
        "properties": {
          "branch": {
            "title": "Branch",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "integer"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "owner": {
            "title": "Owner",
            "type": "string"
          },
          "path": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Path"
          }
        },
        "required": [
          "id",
          "owner",
          "name",
          "branch",
          "path",
          "created_at"
        ],
        "title": "RepoResponse",
        "type": "object"
      },
      "RuleMetadata": {
        "description": "Metadata for a single registered rule function.\n\nAttributes:\n    rule_type: Unique rule identifier (used in policy rule_type field).\n    name: Human-readable rule name.\n    description: What the rule checks.\n    params_schema: Parameter schema describing required/optional params.\n    example_params: Example parameter values for the rule.\n    applicable_enforcement_points: Enforcement points where this rule\n        can be applied.",
        "properties": {
          "applicable_enforcement_points": {
            "items": {
              "type": "string"
            },
            "title": "Applicable Enforcement Points",
            "type": "array"
          },
          "description": {
            "title": "Description",
            "type": "string"
          },
          "example_params": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Example Params"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "params_schema": {
            "additionalProperties": true,
            "title": "Params Schema",
            "type": "object"
          },
          "rule_type": {
            "title": "Rule Type",
            "type": "string"
          }
        },
        "required": [
          "rule_type",
          "name",
          "description",
          "params_schema",
          "applicable_enforcement_points"
        ],
        "title": "RuleMetadata",
        "type": "object"
      },
      "RulesListResponse": {
        "description": "Response schema for GET /policies/rules.\n\nAttributes:\n    rules: List of all registered rule function metadata.",
        "properties": {
          "rules": {
            "items": {
              "$ref": "#/components/schemas/RuleMetadata"
            },
            "title": "Rules",
            "type": "array"
          }
        },
        "required": [
          "rules"
        ],
        "title": "RulesListResponse",
        "type": "object"
      },
      "Severity": {
        "description": "Severity levels for policies and incidents.\n\nPython uses UPPER_CASE, database/API uses lowercase.\n\nAttributes:\n    UNCLASSIFIED: Not yet classified.\n    LOW: Low severity - informational.\n    MEDIUM: Medium severity - should be addressed.\n    HIGH: High severity - requires attention.\n    CRITICAL: Critical severity - immediate action required.",
        "enum": [
          "unclassified",
          "low",
          "medium",
          "high",
          "critical"
        ],
        "title": "Severity",
        "type": "string"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      }
    },
    "securitySchemes": {
      "AgentKey": {
        "description": "An agent API key (kind=agent).",
        "scheme": "bearer",
        "type": "http"
      },
      "AnyCredential": {
        "description": "An agent API key, CLI API key, or user session JWT.",
        "scheme": "bearer",
        "type": "http"
      },
      "HumanSession": {
        "description": "A user session JWT; API keys are refused.",
        "scheme": "bearer",
        "type": "http"
      },
      "UserCredential": {
        "description": "A user session JWT or CLI API key (kind=cli).",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "AI Compliance Logging & Policy Enforcement",
    "title": "Kyvvu API",
    "version": "1.0.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/agents": {
      "get": {
        "description": "List all agents with optional filtering.\n\nRetrieves paginated list of agents with optional environment and activity filters.\n\n**Active/Inactive Filtering:**\nBy default, only active agents are returned. Use `include_inactive=true` to\nsee both active and inactive agents.\n\n**Environment Filtering:**\nFilter by environment using the `environment` parameter. Valid values:\n- development\n- qa\n- staging\n- production\n\n**Pagination:**\nUses offset-based pagination with `skip` and `limit` parameters:\n- `skip`: Number of records to skip (default: 0)\n- `limit`: Maximum records to return (default: 100, max: 1000)\n\n**Example Request:**\n```\nGET /api/v1/agents?environment=production&include_inactive=false&skip=0&limit=50\n```\n\n**Example Response:**\n```json\n[\n  {\n    \"agent_id\": \"agent_a1b2c3d4\",\n    \"agent_key\": \"customer-support-bot\",\n    \"name\": \"Customer Support Agent\",\n    \"purpose\": \"Handles customer inquiries\",\n    \"owner_id\": \"user@example.com\",\n    \"risk_classification\": \"high\",\n    \"maintainer_id\": \"user@example.com\",\n    \"metadata\": {\"version\": \"1.2.0\"},\n    \"environment\": \"production\",\n    \"active\": true,\n    \"created_at\": \"2024-12-23T10:30:00Z\",\n    \"updated_at\": \"2024-12-23T10:35:00Z\"\n  }\n]\n```",
        "operationId": "Agents_list_agents",
        "parameters": [
          {
            "in": "query",
            "name": "environment",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Environment"
            }
          },
          {
            "in": "query",
            "name": "include_inactive",
            "required": false,
            "schema": {
              "default": false,
              "title": "Include Inactive",
              "type": "boolean"
            }
          },
          {
            "description": "Number of items to skip for pagination",
            "in": "query",
            "name": "skip",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of items to skip for pagination",
              "minimum": 0,
              "title": "Skip",
              "type": "integer"
            }
          },
          {
            "description": "Maximum number of items to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum number of items to return",
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/AgentListResponse"
                  },
                  "title": "Response Agents List Agents",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "List Agents",
        "tags": [
          "Agents"
        ]
      },
      "post": {
        "description": "Register or update an agent.\n\nCalled by the Python client to register an agent with the platform.\n\nSetting `store_metadata=false` deletes previously stored name, purpose,\ndescription, owner_id, maintainer_id, and metadata from the agent record.\n\n**owner_id vs maintainer_id:**\n- `owner_id` \u2014 the email of the verified org member accountable for this agent (EU AI Act Article 16).\n  If provided, it must match the email of a verified member of your organisation. If omitted, it is\n  auto-populated from the API key owner's email \u2014 the principal that registered the agent is the\n  accountable party. Not populated when `store_metadata=false`, because then nothing is stored.\n- `maintainer_id` \u2014 free-text; can be any person, team name, or contact handle. Not validated,\n  and not auto-populated: an omitted maintainer stays null.\n\n**Baseline policies:**\nRegistration applies the platform's bundled baseline manifests to the agent, so its\nfirst step is governed without any manual assignment. Applying them is idempotent \u2014\nre-registering neither duplicates nor mutates them \u2014 and an owner who unassigns the\nbaseline keeps that decision: it is not re-applied.\n\n**Agent Reactivation:**\nIf an agent was previously deactivated (soft-deleted), registering it again will\nautomatically reactivate it.\n\n**Agent ID:**\nThe platform assigns the agent_id (`agent_` and 16 hex characters) when the agent is\nfirst registered. Registering the same agent_key again in the same workspace answers\n`registration_status: \"updated\"` with the same agent_id, so an agent can register at\nevery start and read its id from the response.\nRegistration with a different live API key receives 409; an administrator can\nchange the binding at `PUT /api/v1/agents/{agent_id}/api-key`.\n\n**Incidents:**\nThe return object contains a list of potential incidents, violations of active policies,\nrelated to agent registration.\n\n**To get your agent_id:**\n1. Call this endpoint with your agent_key\n2. The response includes the generated agent_id\n3. Use this agent_id in subsequent API calls (e.g., GET /agents/{agent_id})\n\n**Example Request:**\n```json\n{\n  \"agent_key\": \"customer-support-bot\",\n  \"name\": \"Customer Support Agent\",\n  \"purpose\": \"Handles customer inquiries and support tickets\",\n  \"risk_classification\": \"high\",\n  \"environment\": \"production\"\n}\n```\n\n**Example Response:**\n```json\n{\n  \"agent_id\": \"agent_a1b2c3d4e5f6\",\n  \"agent_key\": \"customer-support-bot\",\n  \"name\": \"Customer Support Agent\",\n  \"purpose\": \"Handles customer inquiries and support tickets\",\n  \"owner_id\": \"user@example.com\",\n  \"risk_classification\": \"high\",\n  \"maintainer_id\": \"user@example.com\",\n  \"metadata\": null,\n  \"environment\": \"production\",\n  \"active\": true,\n  \"created_at\": \"2024-12-23T10:30:00Z\",\n  \"updated_at\": \"2024-12-23T10:30:00Z\",\n  \"registration_status\": \"created\",\n  \"incidents\": []\n}\n```",
        "operationId": "Agents_register_agent",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentRegistrationResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "426": {
            "content": {
              "application/json": {
                "example": {
                  "code": "client_upgrade_required",
                  "detail": "This platform serves kyvvu-engine 0.13.0 or later and policy schema 1 or later; this client announced kyvvu-engine 0.0.0, policy schema 0. Upgrade the client.",
                  "min_engine_version": "0.13.0",
                  "min_policy_schema": 1,
                  "upgrade": "pip install -U kyvvu 'kyvvu-engine>=0.13.0'"
                }
              }
            },
            "description": "client_upgrade_required: the announced engine or policy schema is below the platform floor."
          }
        },
        "security": [
          {
            "AgentKey": []
          }
        ],
        "summary": "Register Agent",
        "tags": [
          "Agents"
        ]
      }
    },
    "/api/v1/agents/{agent_id}/api-key": {
      "put": {
        "description": "A workspace administrator can bind an agent to another active agent API key in the same workspace. The key must allow this agent's agent_key. Re-registering with a different live key cannot change the binding.",
        "operationId": "Agents_rebind_agent_api_key",
        "parameters": [
          {
            "in": "path",
            "name": "agent_id",
            "required": true,
            "schema": {
              "title": "Agent Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentApiKeyRebind"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HumanSession": []
          }
        ],
        "summary": "Administrator Rebind Agent API Key",
        "tags": [
          "Agents"
        ]
      }
    },
    "/api/v1/assignments": {
      "get": {
        "description": "List active manifest assignments for the user's organisation.\n\nOptionally filter by agent_id or repo_id.",
        "operationId": "Assignments_list_assignments_endpoint",
        "parameters": [
          {
            "description": "Filter by agent_id hash",
            "in": "query",
            "name": "agent_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by agent_id hash",
              "title": "Agent Id"
            }
          },
          {
            "description": "Filter by repo integer PK",
            "in": "query",
            "name": "repo_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by repo integer PK",
              "title": "Repo Id"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/AssignmentResponse"
                  },
                  "title": "Response Assignments List Assignments Endpoint",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "List Assignments",
        "tags": [
          "Assignments"
        ]
      },
      "post": {
        "description": "Assign a YAML manifest from a connected repository to an agent.\n\nFetches the manifest from GitHub, validates it, and creates Policy rows\nwith the agent's string agent_id. The engine picks up these policies\nautomatically via the existing policy fetch endpoint.\n\n**Risk filter:** Policies whose risk_classification exceeds the agent's\nrisk level are skipped during assignment.\n\n**Restoring the bundled baseline:** omit `repo_id` and pass the bundled\nmanifest's path (as listed by `GET /api/v1/baseline`) to put back a baseline\nmanifest that was unassigned. Registration never does this for you. Any other\npath without a `repo_id` is rejected with 422.\n\n**Duplicate detection:** Returns 409 if the manifest is already assigned\nto this agent.",
        "operationId": "Assignments_create_assignment",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssignmentCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssignmentResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "Assign Manifest to Agent",
        "tags": [
          "Assignments"
        ]
      }
    },
    "/api/v1/assignments/{assignment_id}": {
      "delete": {
        "description": "Remove a manifest assignment from an agent.\n\nSoft-deletes the assignment and disables the associated policies.\nThe policies remain in the database for audit purposes.",
        "operationId": "Assignments_delete_assignment",
        "parameters": [
          {
            "in": "path",
            "name": "assignment_id",
            "required": true,
            "schema": {
              "title": "Assignment Id",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "404": {
            "description": "Resource not found"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "Unassign Manifest",
        "tags": [
          "Assignments"
        ]
      }
    },
    "/api/v1/auth/cli/session": {
      "delete": {
        "description": "Revoke CLI personal access token(s) for the caller.\n\nAccepts a JWT **or** the caller's own `kind=\"cli\"` API key -- the one\nexception to the rule that a CLI PAT can never reach a credential-issuing\nendpoint (self-revoke, parameterless, derives its target entirely from the\npresented credential).\n\n- **Called with a JWT**: revokes every `kind=\"cli\"` key belonging to the\n  caller's account.\n- **Called with a CLI PAT**: revokes only that one key.\n\nNo request body, no id parameter.",
        "operationId": "Authorization_revoke_cli_session",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Authorization Revoke Cli Session",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "Revoke CLI Session(s)",
        "tags": [
          "Authorization"
        ]
      }
    },
    "/api/v1/auth/cli/start": {
      "post": {
        "description": "Start the CLI device-code login flow (RFC 8628 Device Authorization Grant).\n\nNo authentication required -- the CLI has no credential yet. Returns a\nlong ``device_code`` (for the CLI to poll `POST /auth/cli/token` with) and a\nshort ``user_code`` (pre-filled into the ``verification_uri_complete`` page\nthe CLI opens automatically; also shown in the CLI's terminal fallback).\n\n**Example Request:**\n```json\n{\"device_label\": \"vadim-macbook-pro\"}\n```\n\n**Example Response:**\n```json\n{\n  \"device_code\": \"AbCdEf...\",\n  \"user_code\": \"7K2M9XQP\",\n  \"verification_uri\": \"http://localhost:3000/cli-auth.html\",\n  \"verification_uri_complete\": \"http://localhost:3000/cli-auth.html?user_code=7K2M9XQP\",\n  \"expires_in\": 900,\n  \"interval\": 5\n}\n```",
        "operationId": "Authorization_cli_start",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceCodeStartRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceCodeStartResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Start CLI Device Authorization",
        "tags": [
          "Authorization"
        ]
      }
    },
    "/api/v1/auth/cli/token": {
      "post": {
        "description": "Exchange an approved device_code for a CLI personal access token.\n\nNo authentication required -- the CLI has no credential yet; that's the\npoint of this exchange. The CLI is expected to poll this endpoint at the\n``interval`` given by `POST /auth/cli/start` until it gets a terminal\nresponse.\n\n**Success (200):**\n```json\n{\"access_token\": \"KvKey-...\", \"token_type\": \"cli_pat\"}\n```\nThe token is a `kind=\"cli\"` personal access token (90-day expiry), shown\nhere exactly once -- store it securely.\n\n**Errors (400), RFC 8628 \u00a73.5-style:**\n- `{\"error\": \"authorization_pending\"}` -- still waiting on\n  `/auth/cli/approve` or `/auth/cli/deny`. Keep polling.\n- `{\"error\": \"access_denied\"}` -- the user denied this login.\n- `{\"error\": \"expired_token\"}` -- the device_code expired before approval.\n- `{\"error\": \"invalid_grant\"}` -- unknown device_code, or it was already\n  exchanged for a token (a retried/duplicate poll after a prior successful\n  exchange).\n- `{\"error\": \"slow_down\"}` -- RFC 8628 \u00a73.5: polled faster than the\n  advertised ``interval``. Increase the polling interval by 5 seconds.",
        "operationId": "Authorization_cli_token",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceCodeTokenRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "CLI Device Token Exchange",
        "tags": [
          "Authorization"
        ]
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "description": "Get the authenticated user's identity and verification status.\n\nUnlike every other authenticated endpoint in this API, this route does\n**not** reject an account that cannot act -- it is the one endpoint such\nan account can still call, so the dashboard can explain WHICH of the two\nthings is true instead of showing a bare 403.\n\n`is_capped` is about the account and the platform being full: only Kyvvu\nstaff can release it. `is_suspended` is about this person in this\nworkspace: only an administrator there can lift it. They replaced a\nsingle `is_verified`, which meant either, so whichever remedy a screen\noffered was wrong half the time.\n\n**Example Response:**\n```json\n{\n  \"email\": \"user@example.com\",\n  \"org_id\": 1,\n  \"is_capped\": false,\n  \"is_suspended\": false,\n  \"name\": \"Jane Doe\"\n}\n```",
        "operationId": "Authorization_get_current_user",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthMeResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "Get Current User",
        "tags": [
          "Authorization"
        ]
      }
    },
    "/api/v1/incidents": {
      "post": {
        "description": "Receive policy violation incidents posted by the kyvvu-engine.\n\nCalled automatically by ``KyvvuRunner._maybe_report_incident()`` and\n``KyvvuRunner._maybe_report_registration_incident()`` after in-process\npolicy evaluation detects a violation. Authentication uses the agent's\n``Bearer KvKey-...`` API key.\n\n**Deduplication behaviour**\n\nBoth enforcement points group active or ignored incidents by agent, policy (or\nreported name when unresolved), and environment. Repeated violations update\n``last_occurrence`` and ``occurrence_count``.\n\n**Policy lookup**\n\nPolicies are matched by ``policy_id`` within the agent's workspace. When no\nid is sent, or it does not resolve, the lookup uses ``manifest_id`` (the source\nassignment) with ``policy_name``, and then falls back to ``policy_name`` alone\n(narrowed by ``manifest_name`` when given), which links only when exactly one\npolicy matches. If no policy\nresolves, the incident retains the reported policy metadata with a null\n``policy_id``.",
        "operationId": "Incidents_create_engine_incidents",
        "parameters": [
          {
            "in": "header",
            "name": "x-kyvvu-wire-version",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "X-Kyvvu-Wire-Version"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EngineIncidentCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngineIncidentResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "AgentKey": []
          }
        ],
        "summary": "Report Engine Incidents",
        "tags": [
          "Incidents"
        ]
      }
    },
    "/api/v1/logs/batch": {
      "post": {
        "description": "Ingest a completed task as an ordered batch of behavior steps.\n\nUsed by ``KyvvuRunner.end_task()`` to persist the full task history in a\nsingle call after the task completes. The server serialises batches for one\n`(agent_id, task_id)` with a transaction-scoped lock, reads the stored tail,\nverifies (or computes) the hash chain and inserts every entry in the same\ntransaction. The 201 is sent only after that transaction commits, so an\nacknowledged batch is always stored; a failed write returns 5xx and stores\nnothing.\n\nHash input fields are `agent_id`, `task_id`, `seq`, `input_data` (the stored\n`input`, `{}` when absent), and `previous_hash` (when present). The chain does\nnot cover `kind`, `verb`, `timestamp`, `output`, `properties`, or `meta`.\n\n**Who computes the chain.** The Kyvvu engine, Python client and kyvvu-claude send\n`seq` and `hash` on every step: the client computes the chain and the server\nverifies it. Such a batch must continue the stored chain (first `seq` = stored\ntail + 1, contiguous after that) and every `hash` must equal the server's\nrecomputation; otherwise the whole batch is refused with 422 and `detail.code`\n`seq_mismatch` or `hash_mismatch`, naming the first bad step's `index` and\n`seq`, and nothing is stored. The returned `hash_tail` is then the client's own\nchain head. A batch with no `hash` on any step is numbered and hashed by the\nserver, as before; mixing the two in one batch is refused (422).\n\n**Auth**: API key only.\n\nEvery step must include an RFC 3339 `timestamp` with an offset or `Z` and at\nmost six fractional digits; every `step.*` needs an `action_id` unique within\nthe task. Identifiers use `[A-Za-z0-9._:-]` and payloads must be I-JSON. A batch\nthat breaks any rule is refused whole: 422, or 409 for an `action_id` already\nstored. See https://docs.kyvvu.com/integrations/rest-api.\n\n`agent_id` is the platform's identifier for the agent (`agent_...`), as\nreturned by `POST /api/v1/agents`, not the caller's `agent_key`.\n\n**Request body:**\n```json\n{\n  \"agent_id\": \"agent_3f9c2a7b1d4e6f80\",\n  \"task_id\": \"task_xyz\",\n  \"steps\": [\n    {\n      \"kind\": \"task.start\",\n      \"step_name\": \"task_start\",\n      \"timestamp\": \"2026-04-20T10:00:00Z\"\n    },\n    {\n      \"kind\": \"step.model\",\n      \"action_id\": \"act-1\",\n      \"step_name\": \"call_llm\",\n      \"properties\": {\"model\": \"gpt-4o\"},\n      \"input\": {\"prompt\": \"Hello\"},\n      \"output\": {\"response\": \"Hi\"},\n      \"timestamp\": \"2026-04-20T10:00:01Z\"\n    }\n  ]\n}\n```\n\n**Response:**\n```json\n{\"task_id\": \"task_xyz\", \"steps_logged\": 2, \"hash_tail\": \"abc123...\"}\n```",
        "operationId": "Logs_create_batch_log",
        "parameters": [
          {
            "in": "header",
            "name": "idempotency-key",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maxLength": 128,
                  "minLength": 1,
                  "pattern": "^[\\x21-\\x7e]+$",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Idempotency-Key"
            }
          },
          {
            "in": "header",
            "name": "x-kyvvu-wire-version",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "X-Kyvvu-Wire-Version"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchLogCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchLogResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "409": {
            "content": {
              "application/json": {
                "examples": {
                  "duplicate_action": {
                    "value": {
                      "detail": {
                        "code": "duplicate_action_id",
                        "message": "action_id already recorded in this task: act-1"
                      }
                    }
                  }
                }
              }
            },
            "description": "An action_id already exists in this task. The entire batch is refused."
          },
          "422": {
            "content": {
              "application/json": {
                "examples": {
                  "hash_mismatch": {
                    "value": {
                      "detail": {
                        "code": "hash_mismatch",
                        "expected_hash": "1111111111111111111111111111111111111111111111111111111111111111",
                        "index": 0,
                        "message": "steps[0] (seq 1) has hash 0000000000000000000000000000000000000000000000000000000000000000, but the chain computes 1111111111111111111111111111111111111111111111111111111111111111",
                        "seq": 1
                      }
                    }
                  },
                  "seq_mismatch": {
                    "value": {
                      "detail": {
                        "code": "seq_mismatch",
                        "expected_seq": 2,
                        "index": 0,
                        "message": "steps[0] has seq 3, but this task's chain continues at seq 2",
                        "seq": 3
                      }
                    }
                  },
                  "validation": {
                    "value": {
                      "detail": [
                        {
                          "loc": [
                            "body",
                            "steps",
                            0,
                            "timestamp"
                          ],
                          "msg": "Field required",
                          "type": "missing"
                        }
                      ]
                    }
                  }
                }
              }
            },
            "description": "Invalid payload, identifiers, timestamps, sequence, or hash. No steps are stored."
          }
        },
        "security": [
          {
            "AgentKey": []
          }
        ],
        "summary": "Batch Create Logs",
        "tags": [
          "Logs"
        ]
      }
    },
    "/api/v1/policies": {
      "get": {
        "description": "List policies with optional filtering.\n\nRetrieves paginated list of policies with optional filters by\nenforcement_point, agent_id, and enabled status.\n\n**Pagination:**\nUses offset-based pagination with `skip` and `limit` parameters:\n- `skip`: Number of records to skip (default: 0)\n- `limit`: Maximum records to return (default: 100, max: 1000)\n\n**Filters:**\n- `enforcement_point`: Filter by when policy applies (agent_registration, step_execution)\n- `agent_id`: Filter by specific agent (null = applies to all agents)\n- `risk_classification`: Filter by risk level (high, limited, minimal, unacceptable, unclassified)\n- `enabled`: Filter by enabled status (true/false)\n\n**Example Request:**\n```\nGET /api/v1/policies?enforcement_point=agent_registration&risk_classification=high&enabled=true&skip=0&limit=50\n```\n\n**Example Response:**\n```json\n[\n  {\n    \"id\": 123,\n    \"name\": \"Require Owner ID for High-Risk Agents\",\n    \"description\": \"All high-risk agents must have an owner_id per EU AI Act Article 16\",\n    \"enforcement_point\": \"agent_registration\",\n    \"agent_id\": null,\n    \"risk_classification\": \"high\",\n    \"rule_type\": \"require_field\",\n    \"params\": {\n      \"field_name\": \"owner_id\",\n      \"allow_null\": false\n    },\n    \"severity\": \"high\",\n    \"enabled\": true,\n    \"created_at\": \"2024-12-23T10:30:00Z\",\n    \"updated_at\": \"2024-12-23T10:30:00Z\"\n  }\n]\n```",
        "operationId": "Policies_list_policies",
        "parameters": [
          {
            "in": "query",
            "name": "enforcement_point",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Enforcement Point"
            }
          },
          {
            "in": "query",
            "name": "agent_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Agent Id"
            }
          },
          {
            "in": "query",
            "name": "agent_key",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Agent Key"
            }
          },
          {
            "description": "KyvvuRunner instance ID (required with agent_key)",
            "in": "query",
            "name": "instance",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "KyvvuRunner instance ID (required with agent_key)",
              "title": "Instance"
            }
          },
          {
            "description": "Engine policy_ttl_seconds for usage tracking",
            "in": "query",
            "name": "policy_ttl",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maximum": 86400,
                  "minimum": 0,
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Engine policy_ttl_seconds for usage tracking",
              "title": "Policy Ttl"
            }
          },
          {
            "in": "query",
            "name": "risk_classification",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "enum": [
                    "high",
                    "limited",
                    "minimal",
                    "unacceptable",
                    "unclassified"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Risk Classification"
            }
          },
          {
            "in": "query",
            "name": "environment",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Environment"
            }
          },
          {
            "in": "query",
            "name": "enabled",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Enabled"
            }
          },
          {
            "description": "Include soft-deleted policies",
            "in": "query",
            "name": "include_deleted",
            "required": false,
            "schema": {
              "default": false,
              "description": "Include soft-deleted policies",
              "title": "Include Deleted",
              "type": "boolean"
            }
          },
          {
            "description": "Number of items to skip for pagination",
            "in": "query",
            "name": "skip",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of items to skip for pagination",
              "minimum": 0,
              "title": "Skip",
              "type": "integer"
            }
          },
          {
            "description": "Maximum number of items to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum number of items to return",
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/PolicyResponse"
                  },
                  "title": "Response Policies List Policies",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "426": {
            "content": {
              "application/json": {
                "example": {
                  "code": "client_upgrade_required",
                  "detail": "This platform serves kyvvu-engine 0.13.0 or later and policy schema 1 or later; this client announced kyvvu-engine 0.0.0, policy schema 0. Upgrade the client.",
                  "min_engine_version": "0.13.0",
                  "min_policy_schema": 1,
                  "upgrade": "pip install -U kyvvu 'kyvvu-engine>=0.13.0'"
                }
              }
            },
            "description": "client_upgrade_required: the announced engine or policy schema is below the platform floor."
          }
        },
        "security": [
          {
            "AnyCredential": []
          }
        ],
        "summary": "List Policies",
        "tags": [
          "Policies"
        ]
      }
    },
    "/api/v1/policies/rules": {
      "get": {
        "description": "Get all available rule types and their metadata.\n\nReturns metadata for all registered rule functions that can be used to create policies.\nA **rule** is a generic template (e.g., \"require_field\"), while a **policy** is a\nspecific instantiation of that rule with concrete parameters (e.g., \"Require owner_id\nfor all production agents\").\n\n**Rule Metadata Includes:**\n- Human-readable name\n- Description of what the rule checks\n- Parameter schema (what params the rule needs)\n- Example parameters showing typical usage\n- Applicable enforcement points (agent_registration, step_execution)\n\n**Frontend Usage:**\nThe frontend uses this endpoint to:\n1. Populate the rule type dropdown when creating policies\n2. Show parameter examples and validation requirements\n3. Display applicable enforcement points for each rule type\n\n**Example Request:**\n```\nGET /api/v1/policies/rules\n```\n\n**Example Response:**\n```json\n{\n  \"rules\": [\n    {\n      \"rule_type\": \"require_field\",\n      \"name\": \"Require Field\",\n      \"description\": \"Ensures a specified field is present and non-empty\",\n      \"params_schema\": {\n        \"field_name\": {\"type\": \"string\", \"required\": true},\n        \"allow_null\": {\"type\": \"boolean\", \"default\": false}\n      },\n      \"example_params\": {\n        \"field_name\": \"owner_id\",\n        \"allow_null\": false\n      },\n      \"applicable_enforcement_points\": [\"agent_registration\"]\n    },\n    {\n      \"rule_type\": \"check_risk_classification\",\n      \"name\": \"Check Risk Classification\",\n      \"description\": \"Validates risk classification matches EU AI Act requirements\",\n      \"params_schema\": {\n        \"allowed_values\": {\"type\": \"array\", \"items\": \"string\"}\n      },\n      \"example_params\": {\n        \"allowed_values\": [\"limited\", \"high\", \"unacceptable\"]\n      },\n      \"applicable_enforcement_points\": [\"agent_registration\"]\n    }\n  ]\n}\n```",
        "operationId": "Policies_get_available_rules",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RulesListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          }
        },
        "security": [
          {
            "AnyCredential": []
          }
        ],
        "summary": "Get Available Rules",
        "tags": [
          "Policies"
        ]
      }
    },
    "/api/v1/repos": {
      "get": {
        "description": "List all registered repository connections for the user's organisation.",
        "operationId": "Repos_list_repos_endpoint",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/RepoResponse"
                  },
                  "title": "Response Repos List Repos Endpoint",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "List Repositories",
        "tags": [
          "Repos"
        ]
      }
    },
    "/api/v1/repos/{repo_id}/read": {
      "get": {
        "description": "List YAML/YML files from a connected GitHub repository.\n\nDecrypts the stored token, calls the GitHub Trees API, and returns\nall `.yaml` and `.yml` files found in the repository.\n\n**Truncation:** If the repository tree is too large for a single GitHub API\nresponse, the `truncated` flag will be `true`.",
        "operationId": "Repos_read_repo_files",
        "parameters": [
          {
            "in": "path",
            "name": "repo_id",
            "required": true,
            "schema": {
              "title": "Repo Id",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RepoReadResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "404": {
            "description": "Resource not found"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "Read Repository Files",
        "tags": [
          "Repos"
        ]
      }
    },
    "/api/v1/repos/{repo_id}/read/{file_path}": {
      "get": {
        "description": "Fetch a single file from a connected GitHub repository and validate it as a manifest.\n\nReturns the raw YAML content plus parsed manifest metadata with validation\nresults. If the YAML is malformed, returns `valid: false` with errors.",
        "operationId": "Repos_read_repo_file",
        "parameters": [
          {
            "in": "path",
            "name": "repo_id",
            "required": true,
            "schema": {
              "title": "Repo Id",
              "type": "integer"
            }
          },
          {
            "in": "path",
            "name": "file_path",
            "required": true,
            "schema": {
              "title": "File Path",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RepoFileContentResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "description": "Authentication required \u2014 missing or invalid token/API key"
          },
          "403": {
            "description": "Forbidden \u2014 insufficient permissions for this resource"
          },
          "404": {
            "description": "Resource not found"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "UserCredential": []
          }
        ],
        "summary": "Read Single File",
        "tags": [
          "Repos"
        ]
      }
    }
  },
  "servers": [
    {
      "url": "https://platform.kyvvu.com"
    }
  ],
  "tags": [
    {
      "name": "Authorization"
    },
    {
      "name": "Agents"
    },
    {
      "name": "Policies"
    },
    {
      "name": "Repos"
    },
    {
      "name": "Assignments"
    },
    {
      "name": "Logs"
    },
    {
      "name": "Incidents"
    }
  ]
}
