{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openguardrails.com/schema/0.8/guard-event.schema.json",
  "title": "GuardEvent",
  "description": "A unit observed at an OGR integration point, on the one observed plane: LLM messages. Two kinds: the two halves of a step (one model call). Every field is required; the empty string on an identity field is the explicit 'no assertion'. All fields are flat, top-level snake_case; objects are reserved for inherently structured data (payload).",
  "type": "object",
  "required": [
    "kind",
    "step_id",
    "agent_id",
    "agent_type",
    "agent_workspace",
    "agent_owner",
    "agent_user",
    "llm_protocol",
    "payload"
  ],
  "additionalProperties": false,
  "properties": {
    "kind": {
      "enum": [
        "step/request",
        "step/response"
      ]
    },
    "step_id": {
      "type": "string",
      "minLength": 1,
      "description": "Producer-minted opaque id binding the step/request and step/response of ONE model call. A fresh random id per call; never reused. The one coordinate on the wire — everything above it (session, turn, step numbering) is derived server-side."
    },
    "agent_id": {
      "type": "string",
      "description": "WHICH agent this is, unique within the organization; policy resolution and the inventory key on it. Empty = derived from the API key (identity floor)."
    },
    "agent_type": {
      "type": "string",
      "description": "What KIND of agent — the harness or product name. A label, never an identity. Empty = unlabeled."
    },
    "agent_workspace": {
      "type": "string",
      "description": "The named GROUP of agents this one belongs to — one workspace, one policy set. Empty = the API key's workspace."
    },
    "agent_owner": {
      "type": "string",
      "description": "The agent's builder / responsible party. An accountability attribute, never a policy boundary. Empty = unattributed."
    },
    "agent_user": {
      "type": "string",
      "description": "Who is USING the agent this session/request. An attribute, never a policy boundary. Empty = every session is one user."
    },
    "llm_protocol": {
      "enum": [
        "openai.chat",
        "openai.responses",
        "anthropic.messages",
        "canonical"
      ],
      "description": "Which shape the payload speaks: a raw provider body's protocol, or 'canonical' for the OGR canonical shape (an integration with no provider body in hand, e.g. after stream reassembly). The producer states it; a runtime may verify against the body shape."
    },
    "payload": {
      "type": "object",
      "description": "step/request: the untouched provider request body (or canonical {messages, tools?}). step/response: the untouched, complete provider response body — stream-reassembled if streamed — optionally with an integration-inserted top-level 'timing' key (or canonical {text?, reasoning?, tool_calls?, model?, usage?, timing?})."
    }
  }
}
