{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.kodemed.ch/his_rest/SessionResponse-1.0.schema.json",
  "title": "KodeMed HIS REST — Create Coding Session Response",
  "description": "Acknowledgement body returned by `POST /api/v1/coding/session` after the partner submits a case. Carries the server-side session handle + the iframe redirect URL the partner UI should embed. Does NOT carry case data or grouping result — those come back later via the webhook (see WebhookPayload-1.0). This is exclusively the synchronous ack.",
  "type": "object",
  "required": ["sessionId", "redirectUrl", "expiresAt"],
  "properties": {
    "sessionId": {
      "$ref": "../_common/Identifier.schema.json",
      "description": "Server-generated identifier for the coding session. Used as the path parameter in subsequent endpoints (`/api/v1/coding/session/{sessionId}/...`) and echoed in every webhook fired for this session."
    },
    "redirectUrl": {
      "type": "string",
      "format": "uri",
      "description": "Pre-signed URL the partner UI must embed in an iframe (or open in a new tab) so the coder can review and complete the session. Includes the session token; expires at `expiresAt`."
    },
    "expiresAt": {
      "$ref": "../_common/Timestamp.schema.json",
      "description": "Hard deadline after which the session is auto-cancelled and the `redirectUrl` returns 410 Gone. A `session.expired` webhook fires at this time if the coder never completed the session."
    },
    "instanceId": {
      "$ref": "../_common/OpaqueIdentifier.schema.json",
      "description": "Partner-supplied idempotency key echoed from the SessionCreate request. Present in every response, including 409 Conflict bodies, so the partner can correlate."
    },

    "isExisting": {
      "type": "boolean",
      "description": "True when the server reused an existing active session with the same data hash instead of minting a new one (idempotent re-submit). Read by the DLL (Coding.cs) to skip a redundant launch."
    },
    "status": {
      "type": ["string", "null"],
      "description": "Lifecycle status of the session at response time (e.g. ACTIVE, COMPLETED). Null on the synchronous create ack when not yet tracked."
    },
    "dataHash": {
      "type": ["string", "null"],
      "description": "Server-computed hash of the submitted case data, used for the idempotent-reuse check. Null when not computed."
    },
    "source": {
      "type": ["string", "null"],
      "enum": ["API", "DLL", "BROWSER", null],
      "description": "Channel that created the session, echoed back. Null when not set."
    },

    "hasConflict": {
      "type": "boolean",
      "description": "True on a 409-style body when another active DLL session already exists for the user. The DLL (Coding.cs) opens its session-conflict dialog when this is true."
    },
    "conflictSessionId": {
      "anyOf": [{ "$ref": "../_common/Identifier.schema.json" }, { "type": "null" }],
      "description": "sessionId of the pre-existing conflicting session. Null when hasConflict is false."
    },
    "conflictInstanceId": {
      "anyOf": [{ "$ref": "../_common/OpaqueIdentifier.schema.json" }, { "type": "null" }],
      "description": "instanceId of the pre-existing conflicting session. Null when hasConflict is false."
    },
    "conflictCreatedAt": {
      "anyOf": [{ "$ref": "../_common/Timestamp.schema.json" }, { "type": "null" }],
      "description": "ISO-8601 creation time of the conflicting session. Null when hasConflict is false."
    },
    "conflictExpiresInMinutes": {
      "type": ["integer", "null"],
      "description": "Minutes until the conflicting session auto-expires — the DLL uses this to size its retry wait. Null when hasConflict is false."
    },
    "conflictMessage": {
      "type": ["string", "null"],
      "description": "Localized human-readable conflict description the DLL shows in its dialog. Null when hasConflict is false."
    },

    "targetUserNotified": {
      "type": ["boolean", "null"],
      "description": "When targetUserId was supplied on the request: true if a CODING_SESSION_LAUNCH was delivered to at least one connected CodingClient of the target user. Null when targetUserId was not used."
    }
  },
  "unevaluatedProperties": false,
  "examples": [
    {
      "sessionId":   "0f9ea092-c774-4624-bb10-8910ec03d1db",
      "redirectUrl": "https://kodemed.example.com/coding/session/0f9ea092-c774-4624-bb10-8910ec03d1db?token=…",
      "expiresAt":   "2026-06-01T20:00:00Z",
      "instanceId":  "his-2026-06-01-12345",
      "isExisting":  false,
      "status":      null,
      "dataHash":    null,
      "source":      "API",
      "hasConflict": false,
      "conflictSessionId":        null,
      "conflictInstanceId":       null,
      "conflictCreatedAt":        null,
      "conflictExpiresInMinutes": null,
      "conflictMessage":          null,
      "targetUserNotified":       null
    },
    {
      "sessionId":   "0f9ea092-c774-4624-bb10-8910ec03d1db",
      "redirectUrl": "https://kodemed.example.com/coding/session/0f9ea092-c774-4624-bb10-8910ec03d1db?token=…",
      "expiresAt":   "2026-06-01T20:00:00Z",
      "instanceId":  "his-2026-06-01-12345",
      "isExisting":  false,
      "hasConflict": true,
      "conflictSessionId":        "7c2f1a40-0b3e-4a11-9e22-1f0c5d8e4b6a",
      "conflictInstanceId":       "his-2026-06-01-12300",
      "conflictCreatedAt":        "2026-06-01T19:30:00Z",
      "conflictExpiresInMinutes": 42,
      "conflictMessage":          "An active coding session already exists for this user."
    }
  ]
}
