{
  "openapi": "3.0.0",
  "info": {
    "title": "Event Catalog API",
    "description": "Manages the catalog of business events available in epilot",
    "version": "0.1.0"
  },
  "tags": [
    {
      "name": "Event Catalog",
      "description": "API for managing business event catalog"
    }
  ],
  "security": [
    {
      "EpilotAuth": []
    },
    {
      "EpilotOrg": []
    }
  ],
  "paths": {
    "/v1/events": {
      "get": {
        "operationId": "listEvents",
        "summary": "listEvents",
        "description": "Retrieve list of available business events",
        "tags": [
          "Event Catalog"
        ],
        "responses": {
          "200": {
            "description": "A JSON array of event objects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EventConfig"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createCustomEvent",
        "summary": "createCustomEvent",
        "description": "Reserve an org-scoped custom event name and persist its immutable v1.0 draft definition. Custom events are always projected from an entity graph: entity_graph is required and, in guided mapping mode, every schema field needs a graph_source expression. Caller-supplied trigger fields are ignored for custom events.",
        "tags": [
          "Event Catalog"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCustomEventPayload"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Custom event draft created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventConfig"
                }
              }
            }
          },
          "409": {
            "description": "Name is reserved by a built-in or existing org event"
          }
        }
      }
    },
    "/v1/events/{event_name}": {
      "get": {
        "operationId": "getEvent",
        "summary": "getEvent",
        "description": "Retrieve the configuration of a specific business event",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Event configuration object",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventConfig"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "patchEvent",
        "summary": "patchEvent",
        "description": "Update the configuration of a specific business event for the organization",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateEventPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated event configuration object",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventConfig"
                }
              }
            }
          },
          "404": {
            "description": "Event not found"
          }
        }
      },
      "put": {
        "operationId": "replaceCustomEventDraft",
        "summary": "replaceCustomEventDraft",
        "description": "Replace the complete v1.0 definition of an org-scoped custom event while it is still an\nunpublished draft. Drafts have no consumers, so their definition is not yet immutable;\nthe event name is the identity and cannot change. Publication remains a separate action.\n",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCustomEventPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated draft event configuration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventConfig"
                }
              }
            }
          },
          "400": {
            "description": "Definition is invalid or the body event_name differs from the path"
          },
          "404": {
            "description": "Custom event not found"
          },
          "409": {
            "description": "Event is built-in or no longer an unpublished draft"
          },
          "422": {
            "description": "Definition could not be stored"
          }
        }
      },
      "delete": {
        "operationId": "deprecateCustomEvent",
        "summary": "deprecateCustomEvent",
        "description": "Soft-deprecate an org-scoped custom event. Definitions and v1.0 history remain readable.",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Custom event deprecated"
          },
          "404": {
            "description": "Event not found"
          },
          "409": {
            "description": "Built-ins cannot be deprecated or lifecycle cannot transition"
          }
        }
      }
    },
    "/v1/events/{event_name}:preview": {
      "post": {
        "operationId": "previewCustomEvent",
        "summary": "previewCustomEvent",
        "description": "Assemble and fully validate a persisted custom-event draft without publishing it.",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TriggerEventPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deterministic preview result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreviewEventResponse"
                }
              }
            }
          },
          "400": {
            "description": "Projection or schema validation failed"
          },
          "404": {
            "description": "Custom event not found"
          }
        }
      }
    },
    "/v1/events/{event_name}:publish": {
      "post": {
        "operationId": "publishCustomEventDefinition",
        "summary": "publishCustomEventDefinition",
        "description": "Conditionally activate an immutable custom-event v1.0 definition.",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublishCustomEventPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Published event configuration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventConfig"
                }
              }
            }
          },
          "409": {
            "description": "Definition is not a draft or base publication choice is invalid"
          }
        }
      }
    },
    "/v1/events/{event_name}/json_schema": {
      "get": {
        "operationId": "getEventJSONSchema",
        "summary": "getEventJSONSchema",
        "description": "Retrieve the JSON Schema of a specific business event. Pass an optional\n`Epilot-Event-Version` header to retrieve a specific version's schema;\nwhen omitted, the event's latest version is returned.\n",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Epilot-Event-Version",
            "in": "header",
            "required": false,
            "description": "Event payload version (`MAJOR.MINOR`). Defaults to the event's latest version.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Event JSON Schema object",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventJsonSchema"
                }
              }
            }
          },
          "404": {
            "description": "Event or version not found"
          }
        }
      }
    },
    "/v1/events/{event_name}/example": {
      "get": {
        "operationId": "getEventExample",
        "summary": "getEventExample",
        "description": "Generate a sample event payload based on the event's JSON Schema. Pass an\noptional `Epilot-Event-Version` header to generate the example for a\nspecific version; when omitted, the event's latest version is used.\n",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Epilot-Event-Version",
            "in": "header",
            "required": false,
            "description": "Event payload version (`MAJOR.MINOR`). Defaults to the event's latest version.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sample event payload object",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "A sample event payload generated from the JSON Schema"
                }
              }
            }
          },
          "404": {
            "description": "Event or version not found"
          }
        }
      }
    },
    "/v1/events/{event_name}/versions": {
      "get": {
        "operationId": "listEventVersions",
        "summary": "listEventVersions",
        "description": "List every known version of an event, along with the `latest`\nand the set of currently `active` versions. See §3.2 of the\nEvent Payload Versioning RFC.\n",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Registry summary for the event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventVersionRegistrySummary"
                }
              }
            }
          },
          "404": {
            "description": "Event not found"
          }
        }
      }
    },
    "/v1/events/{event_name}:history": {
      "post": {
        "operationId": "searchEventHistory",
        "summary": "searchEventHistory",
        "description": "Paginated history of events",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchOptions"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "List of events in the event history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Event"
                      }
                    },
                    "next_cursor": {
                      "type": "object",
                      "description": "Cursor for pagination. Use this to get the next page of results.",
                      "nullable": true,
                      "properties": {
                        "event_time": {
                          "type": "string",
                          "description": "Timestamp from the last event in the current page",
                          "example": "2025-10-31T12:34:56Z"
                        },
                        "event_id": {
                          "type": "string",
                          "description": "Event ID from the last event in the current page",
                          "example": "evt_1234567890abcdef"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/events/{event_name}:history": {
      "post": {
        "operationId": "searchEventHistoryV2",
        "summary": "searchEventHistoryV2",
        "description": "Paginated history of events with projected/lightweight payload (v2).\n\nReturns `EventSummary` objects instead of fully hydrated `Event` objects:\nhydrated entity objects (values carrying `_schema`/`_id`) are reduced to\nreference stubs `{_schema, _id, _title}` — the full entity is recoverable\nvia GET /v2/events/{event_name}/history/{event_id} or by hitting\nentity-api directly with the `_id`.\n",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchOptionsV2"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "List of event summaries in the event history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EventSummary"
                      }
                    },
                    "next_cursor": {
                      "type": "object",
                      "description": "Cursor for pagination. Use this to get the next page of results.",
                      "nullable": true,
                      "properties": {
                        "event_time": {
                          "type": "string",
                          "description": "Timestamp from the last event in the current page",
                          "example": "2025-10-31T12:34:56Z"
                        },
                        "event_id": {
                          "type": "string",
                          "description": "Event ID from the last event in the current page",
                          "example": "evt_1234567890abcdef"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/events/{event_name}/history/{event_id}": {
      "get": {
        "operationId": "getHistoricalEvent",
        "summary": "getHistoricalEvent",
        "description": "Fetch a single historical event by id with full hydration",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "event_id",
            "in": "path",
            "required": true,
            "description": "Unique event identifier (ULID)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The fully hydrated historical event",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "404": {
            "description": "Event not found"
          }
        }
      }
    },
    "/v1/events/{event_name}:trigger": {
      "post": {
        "operationId": "triggerEvent",
        "summary": "triggerEvent",
        "description": "Explicitly trigger an event by providing input field values and an optional entity seed\nfor graph hydration. The event must be enabled for the organization.\n\n- For events with an entity_graph, a seed (entity_id + node_id) is required\n- For events without an entity_graph, only fields are needed\n- Entity operation context fields (operation, trigger_entity, activity_id, activity_type)\n  are not included when triggering via API\n- Events marked `automation_trigger_only` require `_trigger_source_type: automation` and a\n  stable execution/action ID in `_trigger_source`; retries with that ID return the same\n  logical event, including while durable delivery is pending\n",
        "tags": [
          "Event Catalog"
        ],
        "parameters": [
          {
            "name": "event_name",
            "in": "path",
            "required": true,
            "description": "Unique human readable name of the event",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TriggerEventPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event published successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TriggerEventResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (validation error, missing seed, or missing required fields)"
          },
          "403": {
            "description": "Event is disabled for this organization"
          },
          "404": {
            "description": "Event not found"
          },
          "409": {
            "description": "The Automation trigger source was already used with different trigger input, including a different entity seed"
          },
          "425": {
            "description": "The hydrated entity graph is not ready yet, another request currently owns the\nidempotency lease, or a durably queued event (including an already-requested redrive)\nis awaiting publication. Automation must retry with the same trigger source and may\nadvance only after a 200 response.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds before the Automation action should retry",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Durable delivery exhausted its bounded automatic redrives. The same logical event is\nretained and no new event is created. An API retry with the same trigger source\nrequests a redrive of the existing Standard workflow from its failed task checkpoint\nand receives this retryable response. Subsequent retries receive 425 while that\nredrive is pending or running.\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds before the Automation action should retry",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "EpilotAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization header with epilot OAuth2 bearer token",
        "bearerFormat": "JWT"
      },
      "EpilotOrg": {
        "description": "Overrides the target organization to allow shared tenantaccess",
        "name": "x-epilot-org-id",
        "in": "header",
        "type": "apiKey"
      }
    },
    "schemas": {
      "EventConfigBase": {
        "type": "object",
        "description": "Base properties shared between EventConfig and UpdateEventPayload",
        "properties": {
          "event_name": {
            "type": "string",
            "description": "Unique human readable name of the event",
            "example": "AddMeterReading"
          },
          "event_title": {
            "type": "string",
            "description": "Human-friendly title for the event",
            "example": "Add Meter Reading"
          },
          "event_description": {
            "type": "string",
            "description": "Description of when the event is triggered",
            "example": "Triggered when a new meter reading is added"
          },
          "event_version": {
            "type": "string",
            "description": "Event payload version (MAJOR.MINOR)",
            "example": "1.0"
          },
          "event_status": {
            "type": "string",
            "description": "Status of the event",
            "enum": [
              "active",
              "deprecated",
              "draft",
              "disabled"
            ],
            "example": "active"
          },
          "event_tags": {
            "type": "array",
            "description": "Tags associated with the event for categorization and filtering\n\nThe \"builtin\" tag indicates events that are built into the epilot system.\n",
            "items": {
              "type": "string"
            },
            "example": [
              "builtin",
              "metering",
              "erp"
            ]
          },
          "schema_fields": {
            "type": "object",
            "description": "Fields that define the event schema",
            "additionalProperties": {
              "$ref": "#/components/schemas/SchemaField"
            }
          },
          "entity_graph": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GraphDefinition"
              },
              {
                "description": "Optional entity graph definition for resolving related entities"
              }
            ]
          },
          "entity_operation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EntityOperationTrigger"
              },
              {
                "description": "Optional configuration for triggering this event based on entity operations"
              }
            ]
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether this event is enabled for the organization.\nWhen disabled, the event will not be triggered by any means\n(automatic, API, or automation).\nDefaults to true if not specified.\n",
            "default": true,
            "example": true
          },
          "auto_trigger": {
            "type": "boolean",
            "description": "Whether the event should be triggered automatically by built-in logic\n(e.g., portal submissions, entity mutations, EventBridge rules).\nWhen false, the event can still be triggered manually via API or automations.\nOnly meaningful for builtin events that have automatic trigger sources.\nDefaults to true if not specified.\n",
            "default": true,
            "example": true
          },
          "automation_trigger": {
            "type": "boolean",
            "description": "Whether this event can be explicitly triggered by automations.\nWhen true, the event will appear in the automation builder as a\n\"Trigger Event\" action option.\nDefaults to false if not specified.\n",
            "default": false,
            "example": true
          },
          "api_trigger": {
            "type": "boolean",
            "default": true,
            "description": "Allows explicit API-source triggering, not HTTP transport. Automation also uses HTTP. Automation-only events always disable this source."
          },
          "automation_trigger_only": {
            "type": "boolean",
            "description": "Whether explicit triggering is restricted to Automation. When true, callers must pass\n`_trigger_source_type: automation` and a stable `_trigger_source`. The trigger uses\nstrict entity readiness validation and the durable Automation outbox.\nRequires `automation_trigger: true` and cannot be combined with `entity_operation`.\n",
            "default": false,
            "example": true
          },
          "automation_trigger_seed_node": {
            "type": "string",
            "description": "Required entity-graph seed node for an Automation-only trigger. When configured, an\nexplicit trigger using any other node is rejected before hydration.\n",
            "example": "ticket"
          },
          "event_origin": {
            "type": "string",
            "readOnly": true,
            "enum": [
              "builtin",
              "custom"
            ],
            "description": "Definition ownership. Built-ins always win a name collision."
          },
          "mapping": {
            "$ref": "#/components/schemas/EventMapping"
          },
          "lineage": {
            "$ref": "#/components/schemas/CustomEventLineage"
          },
          "success_criteria": {
            "type": "array",
            "description": "Org-defined success criteria for this event: the entity attributes that an\norganization considers must be captured for an event change request to be\ntreated as complete (e.g. for telephony / self-service flows).\n\nAdvisory metadata — event-catalog does NOT require an org to define any and\ndoes NOT enforce them when an event is triggered or published. The org may\ndefine none (empty array or omitted). When provided, each entry is validated\nfor well-formedness on write (see SuccessCriterion).\n",
            "items": {
              "$ref": "#/components/schemas/SuccessCriterion"
            },
            "example": [
              {
                "entity_schema": "contract",
                "attribute": "installment_amount"
              },
              {
                "entity_schema": "billing_account",
                "attribute": "due_date"
              }
            ]
          }
        }
      },
      "EventConfig": {
        "description": "Event configuration with required fields",
        "allOf": [
          {
            "$ref": "#/components/schemas/EventConfigBase"
          },
          {
            "type": "object",
            "required": [
              "event_name",
              "event_version",
              "schema_fields"
            ]
          }
        ]
      },
      "CreateCustomEventPayload": {
        "type": "object",
        "description": "Complete immutable custom-event v1.0 definition projected from a required entity graph. Publication is a separate conditional action.",
        "properties": {
          "event_name": {
            "type": "string",
            "pattern": "^[A-Z][A-Za-z0-9]{2,79}$"
          },
          "event_title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "event_description": {
            "type": "string",
            "maxLength": 2000
          },
          "event_tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "schema_fields": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/CustomSchemaField"
            }
          },
          "entity_graph": {
            "$ref": "#/components/schemas/GraphDefinition"
          },
          "entity_operation": {
            "$ref": "#/components/schemas/EntityOperationTrigger"
          },
          "automation_trigger": {
            "type": "boolean",
            "default": true
          },
          "api_trigger": {
            "type": "boolean",
            "default": true,
            "description": "Allows API-source triggering independently of Automation (which also uses HTTP). Must be false for Automation-only events."
          },
          "automation_trigger_only": {
            "type": "boolean",
            "default": false,
            "description": "Restricts triggering to Automation with durable delivery and strict readiness. Requires automation_trigger true, api_trigger false, and no entity_operation."
          },
          "automation_trigger_seed_node": {
            "type": "string",
            "minLength": 1,
            "description": "Required cardinality-one graph seed for an Automation-only event. Inherited restrictions cannot be changed."
          },
          "mapping": {
            "$ref": "#/components/schemas/EventMapping"
          },
          "lineage": {
            "$ref": "#/components/schemas/CustomEventLineage"
          },
          "example": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "event_name",
          "event_title",
          "schema_fields",
          "entity_graph"
        ]
      },
      "EventMapping": {
        "type": "object",
        "description": "Guided mappings use schema_fields graph_source expressions; raw mode evaluates one JSONata object transform.",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "guided",
              "jsonata"
            ],
            "default": "guided"
          },
          "jsonata": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20000
          }
        },
        "required": [
          "mode"
        ]
      },
      "CustomEventLineage": {
        "type": "object",
        "description": "Optional catalog lineage to a separately named base event. Built-in inheritance is validated against this exact registered version; its trigger restrictions cannot be removed or replaced. It does not replace the base event.",
        "properties": {
          "base_event_name": {
            "type": "string"
          },
          "base_event_version": {
            "type": "string"
          }
        },
        "required": [
          "base_event_name",
          "base_event_version"
        ]
      },
      "PurposeFilterSnapshot": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1
          },
          "display_name": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "id",
          "display_name"
        ]
      },
      "PublishCustomEventPayload": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "auto_trigger": {
            "type": "boolean",
            "default": true
          },
          "base_auto_trigger_enabled": {
            "type": "boolean",
            "description": "Optional publication choice. False disables only the named lineage base event's org auto-trigger overlay."
          }
        }
      },
      "ValidationIssue": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "path",
          "message"
        ]
      },
      "PreviewEventResponse": {
        "type": "object",
        "properties": {
          "payload": {
            "type": "object",
            "additionalProperties": true
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationIssue"
            }
          }
        },
        "required": [
          "payload",
          "errors"
        ]
      },
      "UpdateEventPayload": {
        "type": "object",
        "description": "Mutable org activation overlay. Immutable event definition fields are not accepted.",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "auto_trigger": {
            "type": "boolean"
          },
          "success_criteria": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SuccessCriterion"
            }
          }
        },
        "additionalProperties": false
      },
      "PrimitiveField": {
        "type": "object",
        "description": "A primitive JSON Schema field definition",
        "properties": {
          "json_schema": {
            "type": "object",
            "description": "JSON Schema definition of the field",
            "example": {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp when reading was taken"
            }
          },
          "required": {
            "type": "boolean",
            "description": "Whether this field is required in the event payload"
          },
          "graph_source": {
            "type": "string",
            "description": "JSONata expression to extract the field value from the hydrated entity graph.\n\nThe expression has access to all hydrated graph nodes by their node ID.\nOptional for built-in events, where an unset value must be provided as input when\ntriggering the event. Required for custom events in guided mapping mode.\n\nExamples:\n  - \"ticket.meter_reading_value\" (simple path)\n  - \"contact.email[0].email\" (nested/array access)\n  - \"ticket.reading_timestamp ?? $now()\" (with fallback)\n  - \"$number(meter_counter.reading_value)\" (type coercion)\n",
            "example": "ticket.meter_reading_value"
          }
        },
        "required": [
          "json_schema"
        ]
      },
      "ContextEntity": {
        "type": "object",
        "properties": {
          "entity_schema": {
            "type": "string",
            "example": "meter",
            "description": "Schema slug of the context entity"
          },
          "required": {
            "type": "boolean",
            "description": "Whether this field is required in the event payload"
          }
        },
        "required": [
          "entity_schema"
        ]
      },
      "AttachmentField": {
        "type": "object",
        "description": "A schema field representing file attachments associated with the event.\nPresent in schema_fields for events tagged with \"attachment\".\n",
        "properties": {
          "items": {
            "$ref": "#/components/schemas/EventAttachment"
          },
          "required": {
            "type": "boolean",
            "description": "Whether this field is required in the event payload"
          }
        },
        "required": [
          "items"
        ]
      },
      "CustomSchemaField": {
        "description": "Custom v1 fields support graph-projected JSON Schema values and context entities; attachment semantics are built-in-only.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/PrimitiveField"
          },
          {
            "$ref": "#/components/schemas/ContextEntity"
          }
        ]
      },
      "SchemaField": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/PrimitiveField"
          },
          {
            "$ref": "#/components/schemas/ContextEntity"
          },
          {
            "$ref": "#/components/schemas/AttachmentField"
          }
        ]
      },
      "SuccessCriterion": {
        "type": "object",
        "description": "A single org-defined success criterion: an entity attribute that must be captured\nfor this event's change request to be considered complete.\n\nIdentity is the entity schema plus the attribute name — mirroring the\nEntityOperationTrigger `schema`/`attribute` vocabulary. On write (PATCH), both\n`attribute` and `entity_schema` are required and `entity_schema` must match the\n`schema` of a node in the event's `entity_graph` (else the request is rejected) —\nthis prevents unsatisfiable criteria. The criteria themselves are advisory and are\nnever enforced when an event is triggered or published.\n",
        "properties": {
          "entity_schema": {
            "type": "string",
            "description": "Entity schema slug the attribute belongs to (matches a node schema in the event's entity_graph).",
            "example": "contract"
          },
          "attribute": {
            "type": "string",
            "description": "Attribute name on the entity schema.",
            "example": "installment_amount"
          }
        },
        "required": [
          "entity_schema",
          "attribute"
        ]
      },
      "CommonEventMetadata": {
        "type": "object",
        "description": "Common metadata fields present in all event payloads",
        "example": {
          "_org_id": {
            "type": "string",
            "description": "epilot tenant/organization ID"
          },
          "_event_time": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp when event occurred"
          },
          "_event_id": {
            "type": "string",
            "description": "Unique event identifier (ULID)"
          },
          "_event_name": {
            "type": "string",
            "description": "Event name from catalog"
          },
          "_event_version": {
            "type": "string",
            "description": "Schema version number"
          },
          "_event_source": {
            "type": "string",
            "description": "Source that triggered the event"
          },
          "_ack_id": {
            "type": "string",
            "description": "Unique acknowledgment tracking ID for the event"
          }
        },
        "required": [
          "_org_id",
          "_event_time",
          "_event_id",
          "_event_name",
          "_event_version",
          "_event_source"
        ]
      },
      "EventJsonSchema": {
        "type": "object",
        "description": "JSON Schema declaring the event payload structure",
        "example": {
          "type": "object",
          "properties": {
            "_org_id": {
              "type": "string",
              "description": "epilot tenant/organization ID"
            },
            "_event_time": {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp when event occurred"
            },
            "_event_id": {
              "type": "string",
              "description": "Unique event identifier (ULID)"
            },
            "_event_name": {
              "type": "string",
              "description": "Event name from catalog"
            },
            "_event_version": {
              "type": "string",
              "description": "Event payload version (MAJOR.MINOR)"
            },
            "_event_source": {
              "type": "string",
              "description": "Source that triggered the event"
            },
            "reading_value": {
              "type": "number",
              "description": "The meter reading value"
            },
            "reading_date": {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp when reading was taken"
            },
            "read_by": {
              "type": "string",
              "description": "Name or identifier of who submitted the reading"
            },
            "reason": {
              "type": "string",
              "enum": [
                "regular",
                "move-in",
                "move-out",
                "supplier-change",
                "correction",
                "final"
              ],
              "description": "Reason for the meter reading"
            },
            "direction": {
              "type": "string",
              "enum": [
                "feed-in",
                "feed-out"
              ],
              "description": "Direction of energy flow"
            },
            "source": {
              "type": "string",
              "enum": [
                "portal",
                "360",
                "api",
                "automation"
              ],
              "description": "Source system where reading was submitted"
            },
            "meter_id": {
              "type": "string",
              "format": "uuid",
              "description": "Entity ID of the meter"
            },
            "counter_id": {
              "type": "string",
              "format": "uuid",
              "description": "Entity ID of the meter counter"
            },
            "meter_number": {
              "type": "string",
              "description": "Human-readable meter number"
            },
            "obis_number": {
              "type": "string",
              "description": "OBIS code of the counter"
            },
            "unit": {
              "type": "string",
              "description": "Unit of measurement (e.g., kWh, m3)"
            },
            "customer_id": {
              "type": "string",
              "format": "uuid",
              "description": "Entity ID of the customer"
            },
            "contract_id": {
              "type": "string",
              "format": "uuid",
              "description": "Entity ID of the contract"
            },
            "user_id": {
              "type": "string",
              "description": "ID of the user who submitted the reading"
            },
            "user_email": {
              "type": "string",
              "format": "email",
              "description": "Email of the user who submitted the reading"
            }
          },
          "required": [
            "_org_id",
            "_event_time",
            "_event_id",
            "_event_name",
            "_event_version",
            "_event_source",
            "reading_value",
            "reading_date",
            "read_by",
            "reason",
            "direction",
            "source",
            "meter_id",
            "counter_id",
            "meter_number",
            "obis_number",
            "unit",
            "customer_id",
            "contract_id"
          ]
        }
      },
      "InlineDowngradeStep": {
        "type": "object",
        "description": "One step of an event's inline `_downgrades` chain. Maps the current-version payload to the previous version via a JSONata expression. Stamped by Event Catalog at publish time; executed by consumers during walk-back, never by EC itself.",
        "properties": {
          "to": {
            "type": "string",
            "description": "Version label this step downgrades to (the previous version)."
          },
          "jsonata": {
            "type": "string",
            "description": "JSONata expression mapping the current-shape payload to the previous-shape payload."
          }
        },
        "required": [
          "to",
          "jsonata"
        ]
      },
      "Event": {
        "type": "object",
        "description": "An event instance in the event history",
        "properties": {
          "_org_id": {
            "type": "string",
            "description": "epilot tenant/organization ID"
          },
          "_event_time": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp when event occurred"
          },
          "_event_id": {
            "type": "string",
            "description": "Unique event identifier (ULID)"
          },
          "_event_name": {
            "type": "string",
            "description": "Event name from catalog"
          },
          "_event_version": {
            "type": "string",
            "description": "Event payload version (MAJOR.MINOR)",
            "example": "1.0"
          },
          "_event_source": {
            "type": "string",
            "description": "Source that triggered the event"
          },
          "_trigger_source_type": {
            "type": "string",
            "description": "The type of system that triggered the event.\nCommon values: api, automation, operation, portal_user\n",
            "example": "api"
          },
          "_trigger_source": {
            "type": "string",
            "description": "Identifier of the specific trigger source.\n- For api: User ID or API key identifier\n- For automation: Automation execution ID\n- For operation: Activity ID from the entity operation\n- For portal_user: Portal user email\n"
          },
          "_ack_id": {
            "type": "string",
            "description": "Unique acknowledgment tracking ID for the event.\nUsed to track event delivery and processing status.\n"
          },
          "_downgrades": {
            "type": "array",
            "description": "Inline downgrade chain stamped by Event Catalog at publish time, ordered newest-to-oldest. Present ONLY on multi-version events. Internal versioning transport: consumers (e.g. svc-webhooks) walk the payload back to a pinned version using these JSONata steps, then strip the field before delivery -- end customers never receive it.",
            "items": {
              "$ref": "#/components/schemas/InlineDowngradeStep"
            }
          },
          "_automation_chain": {
            "type": "array",
            "description": "Ordered automation flow ids that caused this event (at most 100), propagated verbatim from the trigger input or the originating entity operation. Internal loop-prevention transport for automation-api; svc-webhooks strips it before delivery.",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "_org_id",
          "_event_time",
          "_event_id",
          "_event_name",
          "_event_version",
          "_event_source"
        ],
        "additionalProperties": true,
        "example": {
          "_org_id": "org_123456",
          "_event_time": "2024-01-01T12:00:00Z",
          "_event_id": "01FZ4Z5FZ5FZ5FZ5FZ5FZ5FZ5F",
          "_event_name": "MeterReading",
          "_event_version": "1.0",
          "_event_source": "api",
          "_trigger_source_type": "api",
          "_trigger_source": "user_123456",
          "reading_value": 123.45,
          "reading_date": "2024-01-01T11:59:00Z",
          "read_by": "John Doe",
          "reason": "regular",
          "direction": "feed-out",
          "source": "portal",
          "meter_id": "550e8400-e29b-41d4-a716-446655440000",
          "counter_id": "660e8400-e29b-41d4-a716-446655440000",
          "meter_number": "MT123456789",
          "obis_number": "1-0:1.8.0",
          "unit": "kWh",
          "customer_id": "770e8400-e29b-41d4-a716-446655440000",
          "contract_id": "880e8400-e29b-41d4-a716-446655440000"
        }
      },
      "EventSummary": {
        "type": "object",
        "description": "A lightweight event summary returned by the v2 history endpoint.\n\nIncludes the standard `_*` metadata fields plus a projected subset of the\nevent payload. Hydrated entity objects (values carrying `_schema` or `_id`)\n— and arrays of such objects — are reduced to reference stubs\n`{_schema, _id, _title}` so consumers can identify and follow up on each\nentity without paying the cost of the full hydrated graph. Fetch\n`GET /v2/events/{event_name}/history/{event_id}` for the full hydration.\n\nProjected scalar payload fields appear as additional top-level properties.\n",
        "properties": {
          "_org_id": {
            "type": "string",
            "description": "epilot tenant/organization ID"
          },
          "_event_time": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp when event occurred"
          },
          "_event_id": {
            "type": "string",
            "description": "Unique event identifier (ULID)"
          },
          "_event_name": {
            "type": "string",
            "description": "Event name from catalog"
          },
          "_event_version": {
            "type": "string",
            "description": "Event payload version (MAJOR.MINOR)",
            "example": "1.0"
          },
          "_event_source": {
            "type": "string",
            "description": "Source that triggered the event"
          },
          "_trigger_source_type": {
            "type": "string",
            "description": "The type of system that triggered the event.\nCommon values: api, automation, operation, portal_user\n",
            "example": "api"
          },
          "_trigger_source": {
            "type": "string",
            "description": "Identifier of the specific trigger source.\n"
          },
          "_ack_id": {
            "type": "string",
            "description": "Unique acknowledgment tracking ID for the event.\n"
          }
        },
        "required": [
          "_org_id",
          "_event_time",
          "_event_id",
          "_event_name",
          "_event_version",
          "_event_source"
        ],
        "additionalProperties": true
      },
      "GraphDefinition": {
        "type": "object",
        "description": "Entity graph definition for resolving related entities",
        "properties": {
          "nodes": {
            "type": "array",
            "description": "List of node definitions in the graph",
            "items": {
              "$ref": "#/components/schemas/GraphNode"
            }
          },
          "edges": {
            "type": "array",
            "description": "List of edge definitions connecting nodes",
            "items": {
              "$ref": "#/components/schemas/GraphEdge"
            }
          }
        },
        "required": [
          "nodes",
          "edges"
        ]
      },
      "GraphNode": {
        "type": "object",
        "description": "A node in the entity graph",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for this node in the graph definition",
            "example": "contact"
          },
          "schema": {
            "type": "string",
            "description": "Entity schema slug for this node",
            "example": "contact"
          },
          "cardinality": {
            "type": "string",
            "enum": [
              "one",
              "many"
            ],
            "description": "Cardinality for this node when hydrated:\n- \"one\": Node can only contain one entity, returns single Entity object\n- \"many\": Node can contain multiple entities, returns array of Entity objects\nIf not specified, defaults to \"many\" (returns array)\n",
            "example": "one"
          },
          "fields": {
            "allOf": [
              {
                "$ref": "#/components/schemas/FieldsParam"
              },
              {
                "description": "Optional array of field names to include in the hydrated entity response for this node.\nWhen specified, only the requested fields plus required internal fields (_id, _schema, _org) will be returned.\nOnly applies when hydrate=true.\n"
              }
            ]
          }
        },
        "required": [
          "id",
          "schema"
        ]
      },
      "GraphEdge": {
        "type": "object",
        "description": "An edge connecting two nodes in the graph",
        "properties": {
          "from": {
            "type": "string",
            "description": "Source node ID",
            "example": "contact"
          },
          "to": {
            "type": "string",
            "description": "Target node ID",
            "example": "billing_account"
          }
        },
        "required": [
          "from",
          "to"
        ]
      },
      "EntityOperationTrigger": {
        "type": "object",
        "description": "Configuration for triggering an event based on entity operations.\n\nWhen an entity operation matches the configured criteria, the event will be triggered.\n- On createEntity: the attribute must be present in the entity payload\n- On updateEntity: the attribute must be in diff.added, diff.updated, or diff.deleted\n- On deleteEntity: the event triggers when the entity is deleted (attributes not checked)\n",
        "properties": {
          "operation": {
            "type": "array",
            "description": "List of entity operations that can trigger this event",
            "items": {
              "type": "string",
              "enum": [
                "createEntity",
                "updateEntity",
                "deleteEntity"
              ]
            },
            "example": [
              "createEntity",
              "updateEntity"
            ]
          },
          "schema": {
            "type": "array",
            "description": "List of entity schema slugs that can trigger this event",
            "items": {
              "type": "string"
            },
            "example": [
              "contact",
              "contract",
              "order"
            ]
          },
          "attribute": {
            "type": "array",
            "description": "Optional list of entity attributes to track for changes.\nIf specified, the event only triggers when these attributes are affected.\n- On createEntity: attribute must be defined in the entity payload\n- On updateEntity: attribute must be in diff.added, diff.updated, or diff.deleted\nIf not specified, all changes to matching entities will trigger the event.\nDerived built-in triggers retain every inherited attribute; additions are OR alternatives.\nAn inherited unfiltered trigger cannot be narrowed by adding attribute filters.\n",
            "items": {
              "type": "string"
            },
            "example": [
              "email",
              "phone",
              "status"
            ]
          },
          "purpose": {
            "type": "array",
            "description": "Optional list of purpose names to filter by.\nThe entity must have at least one matching purpose in its _purpose array.\nPurpose names are matched against the taxonomy classification names (e.g., \"Kündigung\", \"Umzug/Auszug\").\nFor custom events, names are allowed only when inherited unchanged from the actual\npinned built-in lineage version. New purposes must use stable purpose_filters IDs.\nNames and stable IDs are OR alternatives; operation/schema/attribute conditions still apply.\nIf neither purpose nor purpose_filters is specified, the event triggers regardless of purpose.\n",
            "items": {
              "type": "string"
            },
            "example": [
              "Kündigung",
              "Umzug/Auszug"
            ]
          },
          "purpose_filters": {
            "type": "array",
            "description": "Stable purpose IDs plus immutable display-name snapshots for custom events. Additive to inherited purpose names (OR). Inherited unfiltered triggers cannot be narrowed.",
            "items": {
              "$ref": "#/components/schemas/PurposeFilterSnapshot"
            }
          }
        },
        "required": [
          "operation",
          "schema"
        ]
      },
      "SearchOptions": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "description": "Maximum number of results to return",
            "default": 10,
            "minimum": 1,
            "maximum": 25
          },
          "cursor": {
            "type": "object",
            "description": "Cursor for pagination. Use the next_cursor from the previous response to get the next page.",
            "properties": {
              "event_time": {
                "type": "string",
                "description": "Timestamp from the last event in the previous page",
                "example": "2025-10-31 12:34:56"
              },
              "event_id": {
                "type": "string",
                "description": "Event ID from the last event in the previous page",
                "example": "evt_1234567890abcdef"
              }
            }
          },
          "timestamp": {
            "type": "object",
            "description": "Filter events by timestamp range",
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time",
                "description": "Start timestamp in ISO 8601 format",
                "example": "2025-10-01T00:00:00Z"
              },
              "to": {
                "type": "string",
                "format": "date-time",
                "description": "End timestamp in ISO 8601 format",
                "example": "2025-10-31T23:59:59Z"
              }
            }
          },
          "event_id": {
            "type": "string",
            "description": "Filter by specific event ID",
            "example": "evt_1234567890abcdef"
          }
        }
      },
      "SearchOptionsV2": {
        "description": "Search options for the v2 history endpoint.\n\nExtends `SearchOptions` with an optional `fields` projection. When `fields`\nis omitted, the response includes all `_*` metadata plus all scalar payload\nfields (and primitive arrays / empty objects/arrays) after entity stripping.\nWhen `fields` is provided, glob/exclusion semantics from `FieldsParam` apply\nagainst `payload.*` paths; `_*` metadata is always included. Entity stripping\nruns AFTER selection.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/SearchOptions"
          },
          {
            "type": "object",
            "properties": {
              "fields": {
                "$ref": "#/components/schemas/FieldsParam"
              }
            }
          }
        ]
      },
      "FieldsParam": {
        "type": "array",
        "description": "List of entity fields to include or exclude in the response\n\nUse ! to exclude fields, e.g. `!_id` to exclude the `_id` field.\n\nGlobbing and globstart (**) is supported for nested fields.\n",
        "items": {
          "type": "string"
        },
        "example": [
          "_id",
          "_title",
          "first_name",
          "account",
          "!account.*._files",
          "**._product"
        ]
      },
      "TriggerEventPayload": {
        "type": "object",
        "description": "Payload for explicitly triggering an event via API",
        "example": {
          "seed": {
            "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "node_id": "ticket"
          },
          "_trigger_source_type": "automation",
          "_trigger_source": "execution-id/action-id"
        },
        "properties": {
          "seed": {
            "type": "object",
            "description": "Entity seed for graph hydration. Required for events that have an entity_graph defined.\nSpecifies which entity to start graph traversal from.\n",
            "properties": {
              "entity_id": {
                "type": "string",
                "format": "uuid",
                "description": "Entity ID to seed the graph hydration"
              },
              "node_id": {
                "type": "string",
                "description": "Node ID from the event's entity_graph definition that matches\nthe seed entity. Must be a valid node in the event's graph.\n"
              }
            },
            "required": [
              "entity_id",
              "node_id"
            ]
          },
          "fields": {
            "type": "object",
            "description": "Input field values for built-in events. Keys must match the event's\nschema_fields definitions. Values are validated against each\nfield's JSON Schema. Ignored for custom events, whose fields are\nalways projected from the entity graph.\n",
            "additionalProperties": true
          },
          "skip_hydration": {
            "type": "array",
            "description": "Optional list of node IDs to skip during entity graph hydration.\nThese nodes will be null/undefined in the event payload.\n",
            "items": {
              "type": "string"
            }
          },
          "_trigger_source_type": {
            "type": "string",
            "description": "The type of system that triggered the event.\nExamples: api, automation, operation, portal_user\nDefaults to \"api\" if not specified.\n"
          },
          "_trigger_source": {
            "type": "string",
            "description": "Identifier of the specific trigger source.\nExamples: user ID, automation execution ID, activity ID, portal user email\nDefaults to the calling user ID if not specified.\nRequired for events marked `automation_trigger_only` and must remain stable across\naction retries.\n"
          },
          "_automation_chain": {
            "type": "array",
            "description": "Ordered automation flow ids that caused this trigger (at most 100); propagated verbatim onto the published event for automation loop prevention.",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "TriggerEventResponse": {
        "type": "object",
        "description": "Response from triggering an event",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the event was published successfully"
          },
          "event_id": {
            "type": "string",
            "description": "The unique event ID (ULID) assigned to this event"
          },
          "event_bridge_event_id": {
            "type": "string",
            "description": "EventBridge event ID when synchronous delivery or a completed retry provides it"
          }
        },
        "required": [
          "success",
          "event_id"
        ]
      },
      "EventAttachment": {
        "type": "object",
        "description": "A file attachment associated with an event",
        "properties": {
          "entity_id": {
            "type": "string",
            "format": "uuid",
            "description": "Entity ID of the file"
          },
          "filename": {
            "type": "string",
            "description": "Name of the file"
          },
          "mime_type": {
            "type": "string",
            "description": "MIME type of the file (e.g., application/pdf)"
          },
          "size_bytes": {
            "type": "integer",
            "minimum": 0,
            "description": "File size in bytes"
          },
          "s3ref": {
            "type": "object",
            "description": "S3 reference for the file content",
            "properties": {
              "bucket": {
                "type": "string",
                "description": "S3 bucket name"
              },
              "key": {
                "type": "string",
                "description": "S3 object key"
              }
            },
            "required": [
              "bucket",
              "key"
            ]
          },
          "version_index": {
            "type": "integer",
            "description": "File version index (always 0 for newly created files)"
          },
          "readable_size": {
            "type": "string",
            "description": "Human-readable file size (e.g., \"200 KB\")"
          }
        },
        "required": [
          "entity_id",
          "version_index"
        ]
      },
      "FieldChange": {
        "type": "object",
        "description": "A field-level change descriptor. Powers the declarative half of the\nversion DSL.\n",
        "properties": {
          "field": {
            "type": "string",
            "description": "Name of the field affected by this change.",
            "example": "reading"
          },
          "op": {
            "type": "string",
            "enum": [
              "added",
              "removed",
              "type-changed"
            ],
            "description": "Kind of change. Renames are NOT a first-class op — represent them\nas a `removed` + `added` pair (semantic intent goes into\n`change_summary` / `change_notes`).\n"
          },
          "type_old": {
            "type": "string",
            "description": "Type label for the previous shape (for `removed` and `type-changed`)."
          },
          "type_new": {
            "type": "string",
            "description": "Type label for the new shape (for `added` and `type-changed`)."
          }
        },
        "required": [
          "field",
          "op"
        ]
      },
      "VersionMeta": {
        "type": "object",
        "description": "One entry of an event's version timeline.",
        "properties": {
          "version": {
            "type": "string",
            "description": "MAJOR.MINOR version label.",
            "example": "1.0"
          },
          "released_at": {
            "type": "string",
            "description": "ISO 8601 release timestamp.",
            "example": "2025-11-15"
          },
          "change_summary": {
            "type": "string",
            "description": "Required one-liner describing what changed in this version (≤280 chars)."
          },
          "change_notes": {
            "type": "string",
            "description": "Optional longer-form prose (markdown)."
          },
          "changes": {
            "type": "array",
            "description": "Hand-authored list of field-level changes from the previous version. Empty for v1.",
            "items": {
              "$ref": "#/components/schemas/FieldChange"
            }
          }
        },
        "required": [
          "version",
          "released_at",
          "change_summary",
          "changes"
        ]
      },
      "EventVersionRegistrySummary": {
        "type": "object",
        "description": "Summary of an event's version timeline returned by\n`GET /v1/events/{event_name}/versions`.\n",
        "properties": {
          "event_name": {
            "type": "string",
            "example": "MeterReadingAdded"
          },
          "latest": {
            "type": "string",
            "description": "The newest registered version.",
            "example": "1.0"
          },
          "versions": {
            "type": "array",
            "description": "Full version timeline, ordered oldest → newest.",
            "items": {
              "$ref": "#/components/schemas/VersionMeta"
            }
          }
        },
        "required": [
          "event_name",
          "latest",
          "versions"
        ]
      }
    }
  },
  "servers": [
    {
      "url": "https://event-catalog.sls.epilot.io"
    }
  ]
}
