{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.kodemed.ch/his_rest/WebhookPayload-1.0.schema.json",
  "title": "KodeMed HIS REST — Webhook Payload",
  "description": "Body POSTed by the server to the partner's configured webhook URL when a coding session reaches a terminal state (completed / cancelled / expired). HMAC-SHA256 signed with the IntegrationProfile.webhookSecret, signature in `X-KodeMed-Signature: sha256=<hex>` header.\n\n## Model-separation invariant (Ricardo 2026-06-01 on #640)\n\nThe coded `<Fall>` XML — when included — lives in `cases[].data` and validates AGAINST the SPIGES XSD (`Documentation/spiges/do-d-14.04-SPIGES-2025-02.xsd`) as-is. Grouper output (DRG, MDC, PCCL, cost weight) lives in a SEPARATE `codingResult` object next to it, NEVER injected back into the XML. The two models stay disjoint on the wire — a partner that wants only the result skips `data`; one that wants only the case data skips `codingResult`; both are independently validatable.\n\nThis is the exact anti-pattern this schema kills: the legacy DLL serializer (`KodeMed.CodingUI/src/services/spigesParser.ts:743-747`) injected `<GrouperResult drg=… pccl=…/>` directly inside `<Fall>`, producing XML that no longer validated against SPIGES. The webhook contract refuses that shape.",
  "type": "object",
  "allOf": [
    { "$ref": "../_common/CaseData.schema.json" }
  ],
  "required": ["eventType", "sessionId", "instanceId", "occurredAt"],
  "properties": {
    "eventType": {
      "allOf": [
        { "$ref": "../_common/EventType.schema.json" },
        { "enum": ["case.coded", "case.discarded", "session.expired"] }
      ],
      "description": "Discriminator restricted to the webhook-relevant subset. Webhook fires ONLY on terminal session state: `case.coded` (coder pressed Apply), `case.discarded` (coder pressed Discard), `session.expired` (session timed out). The $ref keeps the value in the canonical EventType enum (one source of truth); the enum overlay restricts the subset reachable as a webhook payload (#640 audit CRITICAL #3 — pre-2026-06-02 a non-terminal event like `dll.heartbeat` would have validated as a webhook body)."
    },
    "sessionId": {
      "$ref": "../_common/Identifier.schema.json",
      "description": "Server-side session handle echoed from SessionResponse."
    },
    "instanceId": {
      "$ref": "../_common/OpaqueIdentifier.schema.json",
      "description": "Partner-supplied idempotency key echoed from the original SessionCreate request, so the partner can correlate without keeping a sessionId↔instanceId map."
    },
    "occurredAt": {
      "$ref": "../_common/Timestamp.schema.json",
      "description": "Timestamp the terminal state was reached server-side (NOT when the webhook was delivered)."
    },
    "codingResult": {
      "$ref": "../_common/CodingResult.schema.json",
      "description": "Grouper output + final code list. Present for `eventType=case.coded`; absent (or null) for `case.discarded` / `session.expired`. The contained `caseId` matches the SPIGES `<Fall fall_id>` so partners can map result-to-case even when the webhook covers a multi-Fall submission."
    }
  },
  "unevaluatedProperties": false,
  "examples": [
    {
      "eventType":   "case.coded",
      "sessionId":   "0f9ea092-c774-4624-bb10-8910ec03d1db",
      "instanceId":  "his-eoc-2026-06-01-12345",
      "occurredAt":  "2026-06-01T19:42:11Z",
      "format":      "spiges",
      "schemaVersion": "SPIGES-1.5",
      "data": "<?xml version=\"1.0\"?><spiges><Fall fall_id=\"F001\">…coded SPIGES, NO <GrouperResult> injection…</Fall></spiges>",
      "codingResult": {
        "caseId":     "F001",
        "drg":        "G22C",
        "mdc":        "06",
        "pccl":       3,
        "costWeight": 0.976,
        "diagnoses":  [
          { "code": "K35.2", "catalog": "ICD-10-GM", "isHauptdiagnose": true }
        ],
        "procedures": [
          { "code": "47.01", "catalog": "CHOP", "performedAt": "2026-06-01T11:30:00Z" }
        ],
        "warnings":   []
      }
    },
    {
      "eventType":   "case.discarded",
      "sessionId":   "0f9ea092-c774-4624-bb10-8910ec03d1db",
      "instanceId":  "his-eoc-2026-06-01-12345",
      "occurredAt":  "2026-06-01T19:42:11Z"
    }
  ]
}
