{
  "components": {
    "headers": {
      "LCR-Audit-Event-Id": {
        "description": "Id of the audit event produced by this write (also present as `lastAuditEventId` in the body).",
        "schema": {
          "$ref": "#/components/schemas/EntityId"
        }
      }
    },
    "parameters": {
      "Cursor": {
        "description": "Opaque cursor from a previous page's `page.nextCursor`.",
        "in": "query",
        "name": "cursor",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "description": "Client-generated unique key (UUID recommended) for a retriable write. Semantics (`LCR-FR-093`,\n`LCR-NFR-005`): the first request with a key executes; a retry with the same key **and the same\nrequest body** returns the original outcome (same status, same body, same `LCR-Audit-Event-Id`)\nwithout executing again; the same key with a different body is rejected with `409` and\n`type: .../idempotency-key-reuse`. Keys are scoped to the authenticated principal and retained\nfor at least 7 days. A retry never creates a second business outcome, delivery, notification,\nterminal action or verification outcome. Intentional new work (a correction, a new evidence\nassociation) must use a new key so it is not mistaken for a replay.\n",
        "in": "header",
        "name": "Idempotency-Key",
        "required": true,
        "schema": {
          "maxLength": 128,
          "minLength": 16,
          "type": "string"
        }
      },
      "Limit": {
        "in": "query",
        "name": "limit",
        "schema": {
          "default": 50,
          "maximum": 200,
          "minimum": 1,
          "type": "integer"
        }
      },
      "RecommendationId": {
        "in": "path",
        "name": "recommendationId",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/EntityId"
        }
      },
      "UploadId": {
        "in": "path",
        "name": "uploadId",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/EntityId"
        }
      }
    },
    "responses": {
      "Conflict": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ConflictProblem"
            }
          }
        },
        "description": "Version mismatch (`If-Match`), idempotency-key reuse with a different body, duplicate active order, concurrent resolution, a pending item request on the action, an action that no longer takes an answer, or an answer that takes no decision now. The `current` extension carries the current resource when applicable."
      },
      "Forbidden": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Authenticated but not authorized for this action, role, stream or record. Never discloses whether the record exists beyond what the caller may know."
      },
      "NotFound": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Resource not found or not visible to the caller."
      },
      "PortalAccessRefused": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/PortalRefusalProblem"
            }
          }
        },
        "description": "The link, code or sign-in cannot be used. One uniform body for every reason (expired, used, revoked, forged, wrong association); it carries no Assessment or identity data."
      },
      "RuleViolation": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/RuleProblem"
            }
          }
        },
        "description": "Business-rule guard failed (unapproved transition, missing preview, unconfirmed image, ineligible variant, Risk Improvement, healthy-intake manual create, etc.). `violations[]` names each guard."
      },
      "Throttled": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Rate limit exceeded (external routes are WAF/throttle protected).",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        }
      },
      "Unauthorized": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Missing, expired, revoked or replayed credential."
      },
      "Unavailable": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/UnavailableProblem"
            }
          }
        },
        "description": "Service or a required dependency is unavailable (e.g. database resuming from pause, ADR-009).\nEvery operation can answer it: the role guard resolves the caller's actor from the database\nand every handler that reads or writes LCR data connects to it. While a paused database is\nstill resuming, the body carries `code: database_waking` (type\n`https://lcr.gsr/problems/database-waking`) and `Retry-After` is 15 seconds; the request's\ndatabase work did not start, or was not committed, so a client may retry a read after that\ndelay. A write is never retried automatically by a client.\n",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        }
      },
      "ValidationFailed": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationProblem"
            }
          }
        },
        "description": "Structured validation failure identifying every actionable field error (`LCR-FR-002`)."
      }
    },
    "schemas": {
      "Actor": {
        "properties": {
          "actorId": {
            "description": "Stable id in the owning identity authority (internal user id",
            "model version id).": null,
            "portal identity id": null,
            "type": "string",
            "vendor code": null
          },
          "actorType": {
            "enum": [
              "internal_user",
              "insured",
              "broker",
              "system",
              "vendor",
              "model"
            ],
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "role": {
            "description": "Role in effect at the time of the action (internal actors).",
            "type": "string"
          }
        },
        "required": [
          "actorType"
        ],
        "type": "object"
      },
      "AttestationCreate": {
        "properties": {
          "comment": {
            "type": "string"
          },
          "selectedResponse": {
            "type": "string"
          },
          "statementRef": {
            "description": "Identifier of the exact catalog statement/question presented.",
            "type": "string"
          }
        },
        "required": [
          "statementRef",
          "selectedResponse"
        ],
        "type": "object"
      },
      "AuditMeta": {
        "description": "Present on every persisted resource. `version` increments on every write; `lastAuditEventId` identifies the audit event of the last write.",
        "properties": {
          "createdAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "createdBy": {
            "$ref": "#/components/schemas/Actor"
          },
          "lastAuditEventId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "updatedAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "updatedBy": {
            "$ref": "#/components/schemas/Actor"
          },
          "version": {
            "minimum": 1,
            "type": "integer"
          }
        },
        "required": [
          "version",
          "lastAuditEventId",
          "createdAt",
          "createdBy",
          "updatedAt",
          "updatedBy"
        ],
        "type": "object"
      },
      "Channel": {
        "enum": [
          "email",
          "portal",
          "workbench",
          "in_app"
        ],
        "type": "string"
      },
      "ComplianceIntentState": {
        "description": "Dimension 4: state of the one LCR-level compliance-intent response. Never treated as proof.",
        "enum": [
          "not_requested",
          "requested",
          "answered",
          "missing",
          "late",
          "invalid",
          "corrected",
          "dispositioned"
        ],
        "type": "string",
        "x-lcr-decided": "DR-089 (was TBD-016)"
      },
      "ConflictProblem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Problem"
          },
          {
            "properties": {
              "activeOrderId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "conflictKind": {
                "description": "open_request_exists means an item request on the action is still pending (portalCreateItemRequest, portalPutItemAnswer); not_answerable means the action no longer takes an answer because it is resolved (portalPutItemAnswer). For decideItemAnswer, not_decidable means the answer is not will_not_complete, already_decided means it already has a decision (decideItemRequest also answers it for a request that is no longer pending), and answer_replaced means the homeowner has answered the action again since.",
                "enum": [
                  "version_mismatch",
                  "idempotency_key_reuse",
                  "duplicate_active_order",
                  "concurrently_resolved",
                  "duplicate_assessment",
                  "open_request_exists",
                  "not_answerable",
                  "not_decidable",
                  "already_decided",
                  "answer_replaced"
                ],
                "type": "string"
              },
              "current": {
                "additionalProperties": true,
                "description": "The current representation of the resource when safe to return.",
                "type": "object"
              },
              "currentVersion": {
                "type": "integer"
              }
            },
            "type": "object"
          }
        ]
      },
      "CustomerFacingStatus": {
        "description": "Customer vocabulary mapped from internal states; never exposes internal jargon or ML claims.",
        "enum": [
          "open",
          "awaiting_your_response",
          "evidence_received",
          "under_review",
          "more_evidence_needed",
          "reinspection_arranged",
          "complete",
          "waived_by_underwriting",
          "overdue"
        ],
        "type": "string",
        "x-lcr-decided": "DR-089 (was TBD-016)"
      },
      "EntityId": {
        "description": "Prefixed ULID. Prefixes: `asm` Assessment, `cor` capture order, `med` media asset, `drv` derivative, `dec` decision, `lbl` label, `mob` machine observation, `sob` source observation, `ctr` contradiction, `rec` recommendation, `lcv` LCR version, `rpt` report draft, `msg` message, `mi` message instance, `evs` evidence submission, `vfo` verification outcome, `ntf` notification, `trm` terminal action, `def` definition, `mdl` model version, `job` job, `aud` audit event, `exc` exception, `req` item request, `upl` upload.",
        "examples": [
          "asm_01J8Z3N6Q9K5T2X7M4V1R8B0CD"
        ],
        "pattern": "^[a-z]{2,5}_[0-9A-HJKMNP-TV-Z]{26}$",
        "type": "string"
      },
      "EvidenceModality": {
        "enum": [
          "photo",
          "document",
          "attestation"
        ],
        "type": "string"
      },
      "EvidenceState": {
        "description": "Dimension 6: evidence-processing / verification state of one submission version (PRD §3.2 item 6). Provisional names.",
        "enum": [
          "received",
          "processing",
          "quarantined",
          "invalid",
          "reviewable",
          "rejected",
          "superseded",
          "more_evidence_requested",
          "reinspection_ordered",
          "waived",
          "verified"
        ],
        "type": "string",
        "x-lcr-decided": "DR-091 (was TBD-019)"
      },
      "EvidenceSubmission": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AuditMeta"
          },
          {
            "properties": {
              "artifact": {
                "description": "File evidence (photo/document).",
                "properties": {
                  "assetId": {
                    "$ref": "#/components/schemas/EntityId"
                  },
                  "assetVersion": {
                    "type": "integer"
                  },
                  "byteSize": {
                    "type": "integer"
                  },
                  "fileName": {
                    "type": "string"
                  },
                  "mediaType": {
                    "type": "string"
                  },
                  "pageCount": {
                    "type": "integer"
                  }
                },
                "type": "object"
              },
              "assessmentId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "attestation": {
                "description": "Structured attestation evidence.",
                "properties": {
                  "allowedResponses": {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  "catalogVersion": {
                    "type": "integer"
                  },
                  "responderAuthority": {
                    "type": "string"
                  },
                  "selectedResponse": {
                    "type": "string"
                  },
                  "statement": {
                    "type": "string"
                  },
                  "statementRef": {
                    "type": "string"
                  },
                  "sufficiency": {
                    "enum": [
                      "sufficient",
                      "supplemental"
                    ],
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "batchId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "emailSource": {
                "properties": {
                  "messageId": {
                    "$ref": "#/components/schemas/EntityId"
                  },
                  "triageId": {
                    "$ref": "#/components/schemas/EntityId"
                  }
                },
                "type": "object"
              },
              "modality": {
                "$ref": "#/components/schemas/EvidenceModality"
              },
              "outcomeIds": {
                "items": {
                  "$ref": "#/components/schemas/EntityId"
                },
                "type": "array"
              },
              "processing": {
                "properties": {
                  "processedAt": {
                    "$ref": "#/components/schemas/Timestamp"
                  },
                  "reason": {
                    "type": "string"
                  },
                  "securityOutcome": {
                    "enum": [
                      "pending",
                      "safe",
                      "quarantined",
                      "rejected"
                    ],
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "recommendationId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "sourceChannel": {
                "$ref": "#/components/schemas/Channel"
              },
              "state": {
                "$ref": "#/components/schemas/EvidenceState"
              },
              "submissionId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "submissionVersion": {
                "type": "integer"
              },
              "submittedAt": {
                "$ref": "#/components/schemas/Timestamp"
              },
              "submittedBy": {
                "$ref": "#/components/schemas/Actor"
              },
              "supersededBySubmissionId": {
                "$ref": "#/components/schemas/EntityId"
              }
            },
            "required": [
              "submissionId",
              "assessmentId",
              "recommendationId",
              "modality",
              "sourceChannel",
              "state",
              "submittedBy",
              "submittedAt",
              "submissionVersion"
            ],
            "type": "object"
          }
        ]
      },
      "EvidenceUploadInitRequest": {
        "properties": {
          "confirmation": {
            "description": "Read only while lcr_portal_photo_flow is on for the caller. A batch with a photo must carry exactly \"These photos were taken at this property and show the work as it is today.\" (refused 422 `confirmation_required` without it or with any other text); the text and the time it was given are recorded on the batch. Ignored while the flag is off.",
            "type": "string"
          },
          "files": {
            "items": {
              "$ref": "#/components/schemas/PortalUploadFileSpec"
            },
            "maxItems": 50,
            "minItems": 1,
            "type": "array"
          },
          "note": {
            "type": "string"
          }
        },
        "required": [
          "files"
        ],
        "type": "object",
        "x-lcr-decided": "DR-099 (was TBD-029)"
      },
      "EvidenceUploadSession": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UploadSession"
          },
          {
            "properties": {
              "limits": {
                "description": "Finite file/count/size limits in force (`TBD-029`). While lcr_portal_photo_flow is on for the caller, maxFiles is 50 and allowedMediaTypes adds image/heic, image/heif and application/msword.",
                "properties": {
                  "allowedMediaTypes": {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  "maxBytesPerFile": {
                    "type": "integer"
                  },
                  "maxBytesTotal": {
                    "type": "integer"
                  },
                  "maxFiles": {
                    "type": "integer"
                  }
                },
                "type": "object"
              },
              "recommendationId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "rejected": {
                "description": "Files refused at intake (not an allowed type, over the size limit, or the 11th and later in the batch; the 51st and later while lcr_portal_photo_flow is on, when a request of more than 50 files is refused whole). Each is recorded as an `invalid` submission with an owned exception and gets no presigned PUT; the other files proceed (DR-099).",
                "items": {
                  "$ref": "#/components/schemas/UploadRejection"
                },
                "type": "array"
              }
            },
            "required": [
              "recommendationId",
              "limits"
            ],
            "type": "object"
          }
        ]
      },
      "ImageRange": {
        "properties": {
          "max": {
            "maximum": 5,
            "minimum": 1,
            "type": "integer"
          },
          "min": {
            "maximum": 5,
            "minimum": 1,
            "type": "integer"
          },
          "ruleVersion": {
            "type": "string"
          },
          "source": {
            "enum": [
              "variant_override",
              "essential_default",
              "global"
            ],
            "type": "string"
          }
        },
        "required": [
          "min",
          "max",
          "source"
        ],
        "type": "object"
      },
      "IntentChoice": {
        "description": "The three semantic choices (`DR-033`); customer wording open.",
        "enum": [
          "comply_by_current_dates",
          "comply_with_named_requests",
          "no_intent_to_comply"
        ],
        "type": "string",
        "x-lcr-decided": "DR-089 (was TBD-016)"
      },
      "IntentResponse": {
        "properties": {
          "current": {
            "properties": {
              "choice": {
                "$ref": "#/components/schemas/IntentChoice"
              },
              "comment": {
                "type": "string"
              },
              "lcrVersionId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "namedRequestIds": {
                "items": {
                  "$ref": "#/components/schemas/EntityId"
                },
                "type": "array"
              },
              "respondedAt": {
                "$ref": "#/components/schemas/Timestamp"
              },
              "responder": {
                "$ref": "#/components/schemas/Actor"
              }
            },
            "type": "object"
          },
          "deadline": {
            "format": "date",
            "type": "string"
          },
          "lastAuditEventId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "state": {
            "$ref": "#/components/schemas/ComplianceIntentState"
          },
          "versions": {
            "items": {
              "properties": {
                "choice": {
                  "$ref": "#/components/schemas/IntentChoice"
                },
                "disposition": {
                  "description": "`missing` is the product-recorded version when the deadline passed with no accepted answer (E125-04).",
                  "enum": [
                    "valid",
                    "invalid",
                    "late",
                    "corrected",
                    "missing"
                  ],
                  "type": "string"
                },
                "respondedAt": {
                  "$ref": "#/components/schemas/Timestamp"
                },
                "responder": {
                  "$ref": "#/components/schemas/Actor"
                },
                "version": {
                  "type": "integer"
                }
              },
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "state",
          "versions"
        ],
        "type": "object"
      },
      "IntentResponsePut": {
        "properties": {
          "choice": {
            "$ref": "#/components/schemas/IntentChoice"
          },
          "comment": {
            "type": "string"
          },
          "namedRequestIds": {
            "description": "Item requests this intent refers to (for `comply_with_named_requests`).",
            "items": {
              "$ref": "#/components/schemas/EntityId"
            },
            "type": "array"
          }
        },
        "required": [
          "choice"
        ],
        "type": "object"
      },
      "ItemRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AuditMeta"
          },
          {
            "properties": {
              "assessmentId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "decision": {
                "properties": {
                  "actor": {
                    "$ref": "#/components/schemas/Actor"
                  },
                  "at": {
                    "$ref": "#/components/schemas/Timestamp"
                  },
                  "grantedDate": {
                    "description": "The due date granted on the item (approved date_adjustment or date_and_alternate only).",
                    "format": "date",
                    "type": "string"
                  },
                  "grantedOutcome": {
                    "description": "The alternate mitigation granted on the item (approved alternate_mitigation or date_and_alternate only).",
                    "type": "string"
                  },
                  "outcome": {
                    "enum": [
                      "approved",
                      "rejected"
                    ],
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "kind": {
                "description": "date_and_alternate is one request for more time and a different approach together, carrying requestedDate and requestedOutcome (DR-150).",
                "enum": [
                  "date_adjustment",
                  "alternate_mitigation",
                  "date_and_alternate"
                ],
                "type": "string"
              },
              "reason": {
                "type": "string"
              },
              "recommendationId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "recommendationVersion": {
                "type": "integer"
              },
              "requestId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "requestedAt": {
                "$ref": "#/components/schemas/Timestamp"
              },
              "requestedBy": {
                "$ref": "#/components/schemas/Actor"
              },
              "requestedDate": {
                "format": "date",
                "type": "string"
              },
              "requestedOutcome": {
                "type": "string"
              },
              "routing": {
                "description": "The applied item_request routing profile and the per-destination outcome of each notice (the group notice on submission, the responder notice on decision; E125-05).",
                "properties": {
                  "decisionNotificationId": {
                    "$ref": "#/components/schemas/EntityId"
                  },
                  "destinations": {
                    "items": {
                      "properties": {
                        "audience": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string"
                        },
                        "destinationId": {
                          "$ref": "#/components/schemas/EntityId"
                        },
                        "notice": {
                          "enum": [
                            "submitted",
                            "decided"
                          ],
                          "type": "string"
                        },
                        "notificationId": {
                          "$ref": "#/components/schemas/EntityId"
                        },
                        "outcome": {
                          "enum": [
                            "pending",
                            "sent",
                            "delivered",
                            "failed"
                          ],
                          "type": "string"
                        },
                        "outcomeAt": {
                          "$ref": "#/components/schemas/Timestamp"
                        },
                        "required": {
                          "type": "boolean"
                        },
                        "role": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "type": "array"
                  },
                  "notificationId": {
                    "$ref": "#/components/schemas/EntityId"
                  },
                  "resolvedFrom": {
                    "enum": [
                      "assessment_override",
                      "program_purpose",
                      "client_purpose"
                    ],
                    "type": "string"
                  },
                  "routingProfileId": {
                    "$ref": "#/components/schemas/EntityId"
                  },
                  "routingProfileVersion": {
                    "type": "integer"
                  },
                  "valid": {
                    "description": "False when no valid item_request route reached the approving group; the request is kept and routing_invalid is raised.",
                    "type": "boolean"
                  }
                },
                "type": "object"
              },
              "state": {
                "$ref": "#/components/schemas/RequestState"
              }
            },
            "required": [
              "requestId",
              "assessmentId",
              "recommendationId",
              "kind",
              "state",
              "requestedBy",
              "requestedAt",
              "reason"
            ],
            "type": "object"
          }
        ]
      },
      "ItemRequestCreate": {
        "properties": {
          "kind": {
            "description": "date_and_alternate asks for more time and a different approach in one request and needs both requestedDate and requestedOutcome; it is decided as one (DR-150). It is accepted only while the flag lcr_portal_per_action_answers is on for the caller; while it is off, it is refused with 400 like any unknown kind.",
            "enum": [
              "date_adjustment",
              "alternate_mitigation",
              "date_and_alternate"
            ],
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "requestedDate": {
            "description": "Required for date_adjustment and date_and_alternate.",
            "format": "date",
            "type": "string"
          },
          "requestedOutcome": {
            "description": "Description of the alternate mitigation. Required for alternate_mitigation and date_and_alternate.",
            "type": "string"
          }
        },
        "required": [
          "kind",
          "reason"
        ],
        "type": "object"
      },
      "Message": {
        "properties": {
          "assessmentId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "attachments": {
            "items": {
              "properties": {
                "assetId": {
                  "$ref": "#/components/schemas/EntityId"
                },
                "evidenceSubmissionId": {
                  "$ref": "#/components/schemas/EntityId"
                },
                "fileName": {
                  "type": "string"
                },
                "triageId": {
                  "$ref": "#/components/schemas/EntityId"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "body": {
            "type": "string"
          },
          "channel": {
            "$ref": "#/components/schemas/Channel"
          },
          "deliveryOutcome": {
            "description": "State of the message's notification (the loss-control notice of a portal question, or the delivery of a reply) and the latest attempt's per-destination outcomes (E125-08).",
            "properties": {
              "destinations": {
                "items": {
                  "properties": {
                    "at": {
                      "$ref": "#/components/schemas/Timestamp"
                    },
                    "channel": {
                      "$ref": "#/components/schemas/Channel"
                    },
                    "outcome": {
                      "enum": [
                        "pending",
                        "sent",
                        "delivered",
                        "failed"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "channel",
                    "outcome"
                  ],
                  "type": "object"
                },
                "type": "array"
              },
              "state": {
                "enum": [
                  "created",
                  "dispatching",
                  "delivered",
                  "partially_delivered",
                  "failed"
                ],
                "type": "string"
              }
            },
            "required": [
              "state",
              "destinations"
            ],
            "type": "object"
          },
          "direction": {
            "enum": [
              "inbound",
              "outbound"
            ],
            "type": "string"
          },
          "kind": {
            "enum": [
              "question",
              "reply",
              "delivery",
              "reminder",
              "support",
              "system",
              "proof_email"
            ],
            "type": "string"
          },
          "messageId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "notificationId": {
            "$ref": "#/components/schemas/EntityId",
            "description": "Outbound logical notification and its per-destination outcomes."
          },
          "providerMessageId": {
            "type": "string"
          },
          "recipients": {
            "items": {
              "$ref": "#/components/schemas/Actor"
            },
            "type": "array"
          },
          "recommendationId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "sender": {
            "$ref": "#/components/schemas/Actor"
          },
          "sentAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "subject": {
            "type": "string"
          },
          "threadId": {
            "type": "string"
          }
        },
        "required": [
          "messageId",
          "assessmentId",
          "direction",
          "channel",
          "sender",
          "sentAt",
          "body"
        ],
        "type": "object"
      },
      "MessageCreate": {
        "properties": {
          "body": {
            "minLength": 1,
            "type": "string"
          },
          "inReplyToMessageId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "purpose": {
            "$ref": "#/components/schemas/RoutingPurpose",
            "description": "Internal replies only; determines the governed route."
          },
          "recommendationId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "subject": {
            "type": "string"
          }
        },
        "required": [
          "body"
        ],
        "type": "object"
      },
      "PageMeta": {
        "properties": {
          "nextCursor": {
            "description": "Null when there are no more pages.",
            "type": [
              "string",
              "null"
            ]
          },
          "pageSize": {
            "type": "integer"
          },
          "totalEstimate": {
            "description": "Approximate total for display; not authoritative.",
            "type": "integer"
          }
        },
        "required": [
          "pageSize"
        ],
        "type": "object"
      },
      "PortalAccepted": {
        "properties": {
          "status": {
            "enum": [
              "accepted"
            ],
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "type": "object"
      },
      "PortalActionHistory": {
        "description": "Present only while lcr_portal_case_details is on for the caller. When each step on the action last happened; every value is null until that step happens. People are named by portal persona only.",
        "properties": {
          "answer": {
            "description": "The latest homeowner answer.",
            "enum": [
              "will_do",
              "need_change",
              "will_not_complete",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "answeredAt": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Timestamp"
              },
              {
                "type": "null"
              }
            ]
          },
          "answeredBy": {
            "description": "The persona that gave the latest answer.",
            "enum": [
              "insured",
              "broker",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "requestDecidedAt": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Timestamp"
              },
              {
                "type": "null"
              }
            ],
            "description": "When loss control decided the latest request."
          },
          "requestOutcome": {
            "enum": [
              "approved",
              "rejected",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "requestedAt": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Timestamp"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the latest request was made."
          },
          "sentAt": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Timestamp"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the newest file was sent."
          },
          "sentBy": {
            "description": "The persona that sent the newest file.",
            "enum": [
              "insured",
              "broker",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "verifiedAt": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Timestamp"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the latest verification outcome was recorded."
          },
          "verifiedOutcome": {
            "enum": [
              "verify_complete",
              "request_more_evidence",
              "order_reinspection",
              "waive",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "answer",
          "answeredAt",
          "answeredBy",
          "requestedAt",
          "requestDecidedAt",
          "requestOutcome",
          "sentAt",
          "sentBy",
          "verifiedAt",
          "verifiedOutcome"
        ],
        "type": "object"
      },
      "PortalAssessment": {
        "properties": {
          "assessmentId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "broker": {
            "properties": {
              "agencyName": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "agentName": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "agentName",
              "agencyName"
            ],
            "type": "object"
          },
          "completedAt": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Timestamp"
              },
              {
                "type": "null"
              }
            ],
            "description": "Present only while lcr_portal_case_details is on for the caller. When the last recommendation was resolved, once every recommendation shown is verified complete or waived (the case then reads complete); null before that. Staff still close the record."
          },
          "conditionSummary": {
            "description": "Present only while lcr_portal_case_details is on for the caller. The condition grid as the delivered LCR shows it: checked counts the conditions that carried a decision when the delivered version was frozen, inGoodShape those of them with no recommendation in the delivered version.",
            "properties": {
              "checked": {
                "minimum": 0,
                "type": "integer"
              },
              "inGoodShape": {
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "checked",
              "inGoodShape"
            ],
            "type": "object"
          },
          "deliveredVersion": {
            "properties": {
              "deliveredAt": {
                "$ref": "#/components/schemas/Timestamp"
              },
              "lcrVersionId": {
                "$ref": "#/components/schemas/EntityId"
              }
            },
            "type": "object"
          },
          "frontOfHomePhoto": {
            "anyOf": [
              {
                "properties": {
                  "expiresAt": {
                    "$ref": "#/components/schemas/Timestamp"
                  },
                  "url": {
                    "format": "uri",
                    "type": "string"
                  }
                },
                "required": [
                  "url",
                  "expiresAt"
                ],
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "The first ground photo on the case whose vendor tag contains Front, presigned like the recommendation images; null when there is none."
          },
          "insuredName": {
            "description": "The insured on the case's policy account.",
            "type": [
              "string",
              "null"
            ]
          },
          "intent": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/IntentResponse"
              },
              {
                "additionalProperties": false,
                "properties": {
                  "state": {
                    "enum": [
                      "requested"
                    ],
                    "type": "string"
                  }
                },
                "required": [
                  "state"
                ],
                "type": "object"
              }
            ],
            "description": "With lcr_portal_per_action_answers on for the caller, the same IntentResponse that portalGetIntent returns. With it off, only state, reading requested."
          },
          "needsAttention": {
            "description": "E125-14: open customer-visible exceptions, as the approved Communication Template wording only (never an owner, identifier or internal detail).",
            "items": {
              "properties": {
                "code": {
                  "description": "Stable wording key; the template key is `exception.<code>`.",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "code",
                "message"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "notNeeded": {
            "description": "Present only while lcr_portal_case_details is on for the caller. The conditions counted in conditionSummary.inGoodShape, in grid order.",
            "items": {
              "properties": {
                "conditionCode": {
                  "description": "The grid condition code, `C01` to `C17`.",
                  "type": "string"
                },
                "label": {
                  "description": "The condition name from the published catalog; null when it has none.",
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "text": {
                  "description": "The Essential's notNeededGuidance; null until it is authored.",
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "conditionCode",
                "label",
                "text"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "pdfAvailable": {
            "type": "boolean"
          },
          "policyNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "policyPeriod": {
            "properties": {
              "effectiveDate": {
                "format": "date",
                "type": [
                  "string",
                  "null"
                ]
              },
              "expirationDate": {
                "format": "date",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "effectiveDate",
              "expirationDate"
            ],
            "type": "object"
          },
          "progress": {
            "$ref": "#/components/schemas/PortalProgress"
          },
          "propertyAddress": {
            "description": "The case header (PR B2): this key and policyNumber, policyPeriod, insuredName, broker, frontOfHomePhoto and signedInName are present only while lcr_portal_case_details is on for the caller. Each value is null when nothing sources it, never a default.",
            "type": [
              "string",
              "null"
            ]
          },
          "recommendations": {
            "items": {
              "$ref": "#/components/schemas/PortalRecommendation"
            },
            "type": "array"
          },
          "reference": {
            "type": "string"
          },
          "signedInName": {
            "description": "The signed-in insured's name from the case's insured contact; null when unknown and for a broker.",
            "type": [
              "string",
              "null"
            ]
          },
          "supportBlocked": {
            "type": "boolean"
          },
          "unreadQuestions": {
            "description": "Present only while lcr_portal_case_details is on for the caller. The staff messages in the signed-in responder's question thread sent after its latest read mark (portalMarkQuestionsRead). The responder's own messages never count.",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "assessmentId",
          "deliveredVersion",
          "recommendations",
          "progress",
          "intent",
          "pdfAvailable"
        ],
        "type": "object"
      },
      "PortalAssociatedAssessments": {
        "properties": {
          "items": {
            "items": {
              "properties": {
                "assessmentId": {
                  "$ref": "#/components/schemas/EntityId"
                },
                "customerStatus": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/CustomerFacingStatus"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Present only while lcr_portal_case_details is on for the caller. One status for the case from the statuses of its delivered actions: complete once every shown action is complete or waived, else overdue when any is past due, else open. Null while the case has no delivered LCR."
                },
                "propertyAddress": {
                  "description": "Present only while lcr_portal_case_details is on for the caller; null when no address is sourced.",
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "reference": {
                  "type": "string"
                }
              },
              "required": [
                "assessmentId",
                "reference"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "PortalChallenge": {
        "properties": {
          "challengeId": {
            "type": "string"
          }
        },
        "required": [
          "challengeId"
        ],
        "type": "object"
      },
      "PortalEvidenceList": {
        "properties": {
          "items": {
            "items": {
              "properties": {
                "documentKind": {
                  "enum": [
                    "invoice",
                    "other",
                    null
                  ],
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "fileName": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "kind": {
                  "$ref": "#/components/schemas/EvidenceModality"
                },
                "photoAreaId": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "sentAt": {
                  "$ref": "#/components/schemas/Timestamp"
                },
                "sentBy": {
                  "description": "The persona that sent the file.",
                  "enum": [
                    "insured",
                    "broker"
                  ],
                  "type": "string"
                },
                "status": {
                  "$ref": "#/components/schemas/CustomerFacingStatus"
                }
              },
              "required": [
                "fileName",
                "kind",
                "documentKind",
                "photoAreaId",
                "sentAt",
                "sentBy",
                "status"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "PortalItemAnswer": {
        "description": "The current answer on one delivered action (DR-150): the latest answer, or not_answered when there is none. A value that was never recorded is null.",
        "properties": {
          "answeredAt": {
            "description": "When the current answer was given; null while not_answered.",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "deadlineAt": {
            "$ref": "#/components/schemas/Timestamp",
            "description": "The agree-by deadline, 30 days after the action's delivery at 17:00 America/Chicago (DR-151)."
          },
          "decision": {
            "description": "Loss control's decision on a will_not_complete answer (DR-153); null until one is recorded.",
            "oneOf": [
              {
                "type": "null"
              },
              {
                "enum": [
                  "accepted",
                  "denied"
                ],
                "type": "string"
              }
            ]
          },
          "late": {
            "description": "Whether the current answer came after deadlineAt; null while not_answered.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "reason": {
            "description": "The current answer's reason; null when none was given.",
            "type": [
              "string",
              "null"
            ]
          },
          "request": {
            "description": "The item request a need_change answer created and its state; null for any other answer.",
            "oneOf": [
              {
                "type": "null"
              },
              {
                "properties": {
                  "requestId": {
                    "$ref": "#/components/schemas/EntityId"
                  },
                  "state": {
                    "$ref": "#/components/schemas/RequestState"
                  }
                },
                "required": [
                  "requestId",
                  "state"
                ],
                "type": "object"
              }
            ]
          },
          "state": {
            "enum": [
              "not_answered",
              "will_do",
              "need_change",
              "will_not_complete"
            ],
            "type": "string"
          }
        },
        "required": [
          "state",
          "answeredAt",
          "deadlineAt",
          "late",
          "reason",
          "request",
          "decision"
        ],
        "type": "object"
      },
      "PortalItemAnswerPut": {
        "description": "One answer on one delivered action (DR-150), behind `lcr_portal_per_action_answers`. A blank reason is treated as none.",
        "properties": {
          "answer": {
            "description": "will_do is \"I'll do this\", need_change is \"I need a change\" and will_not_complete is \"I will not complete this\".",
            "enum": [
              "will_do",
              "need_change",
              "will_not_complete"
            ],
            "type": "string"
          },
          "change": {
            "description": "Only with need_change, which needs it with requestedDate, alternateOutcome or both. It becomes one item request: more time is a date_adjustment, a different approach an alternate_mitigation, both one date_and_alternate.",
            "properties": {
              "alternateOutcome": {
                "described.": null,
                "description": "A different approach",
                "maxLength": 4000,
                "type": "string"
              },
              "reason": {
                "description": "Why the change is needed; the item request's reason.",
                "maxLength": 4000,
                "type": "string"
              },
              "requestedDate": {
                "as the date asked for.": null,
                "description": "More time",
                "format": "date",
                "type": "string"
              }
            },
            "required": [
              "reason"
            ],
            "type": "object"
          },
          "reason": {
            "description": "Required for will_not_complete; optional otherwise.",
            "maxLength": 2000,
            "type": "string"
          }
        },
        "required": [
          "answer"
        ],
        "type": "object"
      },
      "PortalItemRequestList": {
        "properties": {
          "items": {
            "items": {
              "properties": {
                "decidedAt": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/Timestamp"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "grantedDate": {
                  "format": "date",
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "grantedOutcome": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "kind": {
                  "enum": [
                    "date_adjustment",
                    "alternate_mitigation",
                    "date_and_alternate"
                  ],
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                },
                "requestId": {
                  "$ref": "#/components/schemas/EntityId"
                },
                "requestedAt": {
                  "$ref": "#/components/schemas/Timestamp"
                },
                "requestedDate": {
                  "format": "date",
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "requestedOutcome": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "state": {
                  "$ref": "#/components/schemas/RequestState"
                }
              },
              "required": [
                "requestId",
                "kind",
                "requestedAt",
                "requestedDate",
                "requestedOutcome",
                "reason",
                "state",
                "decidedAt",
                "grantedDate",
                "grantedOutcome"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "items"
        ],
        "type": "object"
      },
      "PortalLinkReissueRequest": {
        "properties": {
          "linkToken": {
            "type": "string"
          }
        },
        "required": [
          "linkToken"
        ],
        "type": "object"
      },
      "PortalPhotoArea": {
        "properties": {
          "areaId": {
            "description": "The first 16 hex characters of sha256(recommendation id + before image asset id).",
            "type": "string"
          },
          "beforeImage": {
            "anyOf": [
              {
                "properties": {
                  "expiresAt": {
                    "$ref": "#/components/schemas/Timestamp"
                  },
                  "url": {
                    "format": "uri",
                    "type": "string"
                  }
                },
                "required": [
                  "url",
                  "expiresAt"
                ],
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "The signed before image; null when it cannot be signed here."
          },
          "label": {
            "description": "The before image's caption; null when it has none.",
            "type": [
              "string",
              "null"
            ]
          },
          "received": {
            "description": "True when a photo for this area was sent and was not refused, quarantined or invalid.",
            "type": "boolean"
          },
          "required": {
            "description": "Always true; every area needs one after-photo.",
            "type": "boolean"
          }
        },
        "required": [
          "areaId",
          "label",
          "beforeImage",
          "required",
          "received"
        ],
        "type": "object"
      },
      "PortalProgress": {
        "properties": {
          "complete": {
            "type": "integer"
          },
          "items": {
            "items": {
              "properties": {
                "currentDueDate": {
                  "format": "date",
                  "type": "string"
                },
                "recipientNumber": {
                  "type": "integer"
                },
                "recommendationId": {
                  "$ref": "#/components/schemas/EntityId"
                },
                "status": {
                  "$ref": "#/components/schemas/CustomerFacingStatus"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "open": {
            "type": "integer"
          },
          "overdue": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "underReview": {
            "type": "integer"
          }
        },
        "required": [
          "total",
          "complete",
          "open"
        ],
        "type": "object"
      },
      "PortalQuestionsRead": {
        "properties": {
          "readAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "threadId": {
            "type": "string"
          },
          "unreadQuestions": {
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "threadId",
          "readAt",
          "unreadQuestions"
        ],
        "type": "object"
      },
      "PortalQuestionsReadCreate": {
        "properties": {
          "threadId": {
            "description": "A question thread of the session's Assessment (its threadId).",
            "type": "string"
          }
        },
        "required": [
          "threadId"
        ],
        "type": "object"
      },
      "PortalRecommendation": {
        "properties": {
          "actionText": {
            "type": "string"
          },
          "affectedStructure": {
            "type": "string"
          },
          "allowedEvidence": {
            "items": {
              "$ref": "#/components/schemas/EvidenceModality"
            },
            "type": "array"
          },
          "answer": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PortalItemAnswer"
              },
              {
                "type": "null"
              }
            ],
            "description": "The action's current homeowner answer (DR-150), the same PortalItemAnswer that portalGetItemAnswer returns. Present only while lcr_portal_per_action_answers is on for the caller; null when the action has no answer record to show."
          },
          "attestation": {
            "description": "Present when the catalog permits attestation for this item.",
            "properties": {
              "allowedResponses": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "statement": {
                "type": "string"
              },
              "statementRef": {
                "description": "The exact catalog statement presented (`<catalogEntryDefinitionId>:v<versionNumber>`); send it back as `AttestationCreate.statementRef`.",
                "type": "string"
              },
              "submitted": {
                "description": "True when an attestation for this recommendation is already awaiting or under human review.",
                "type": "boolean"
              },
              "sufficiency": {
                "enum": [
                  "sufficient",
                  "supplemental"
                ],
                "type": "string"
              }
            },
            "type": "object"
          },
          "currentDueDate": {
            "format": "date",
            "type": "string"
          },
          "evidenceState": {
            "description": "Customer-facing evidence state (mapped from `EvidenceState`).",
            "type": "string"
          },
          "guidance": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "history": {
            "$ref": "#/components/schemas/PortalActionHistory"
          },
          "images": {
            "items": {
              "properties": {
                "access": {
                  "$ref": "#/components/schemas/PresignedUrl"
                },
                "caption": {
                  "type": "string"
                },
                "highlight": {
                  "$ref": "#/components/schemas/RegionGeometry"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "label": {
            "description": "The recipient reference, `Recommendation N` (DR-030).",
            "type": "string"
          },
          "maintenanceGuidance": {
            "description": "Present only while lcr_portal_case_details is on for the caller. How to keep the action in good shape once done: the published catalog Essential's maintenanceGuidance; null until it is authored.",
            "type": [
              "string",
              "null"
            ]
          },
          "moreEvidenceExplanation": {
            "description": "The reviewer's explanation of what is still needed, present only while the item is `more_evidence_needed` (E126-06, LCR-FR-076).",
            "type": "string"
          },
          "openRequest": {
            "properties": {
              "requestId": {
                "$ref": "#/components/schemas/EntityId"
              },
              "state": {
                "$ref": "#/components/schemas/RequestState"
              }
            },
            "type": "object"
          },
          "originalDueDate": {
            "format": "date",
            "type": "string"
          },
          "photoAreas": {
            "description": "Present only while lcr_portal_photo_flow is on for the caller. One area per before image of the delivered recommendation (its images confirmed for an external audience), each needing one after-photo; empty when the recommendation has no before image.",
            "items": {
              "$ref": "#/components/schemas/PortalPhotoArea"
            },
            "type": "array"
          },
          "recipientNumber": {
            "type": "integer"
          },
          "recommendationId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "status": {
            "$ref": "#/components/schemas/CustomerFacingStatus"
          },
          "statusLabel": {
            "description": "Customer wording for `status` (DR-089; editable by Sam).",
            "type": "string"
          },
          "title": {
            "description": "Present only while lcr_portal_case_details is on for the caller. The action's title, set from its catalog Essential when it was drafted and never edited after, so it is the title delivered; null when the action has none.",
            "type": [
              "string",
              "null"
            ]
          },
          "whyItMatters": {
            "type": "string"
          }
        },
        "required": [
          "recommendationId",
          "recipientNumber",
          "actionText",
          "whyItMatters",
          "currentDueDate",
          "status",
          "evidenceState",
          "allowedEvidence"
        ],
        "type": "object"
      },
      "PortalRecoveryCompleteRequest": {
        "properties": {
          "challengeId": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "newPassword": {
            "minLength": 12,
            "type": "string"
          }
        },
        "required": [
          "challengeId",
          "code",
          "newPassword"
        ],
        "type": "object"
      },
      "PortalRecoveryRequest": {
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "type": "object"
      },
      "PortalRefusalProblem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Problem"
          },
          {
            "properties": {
              "code": {
                "enum": [
                  "portal_access_refused"
                ],
                "type": "string"
              }
            },
            "type": "object"
          }
        ]
      },
      "PortalSession": {
        "properties": {
          "accessToken": {
            "type": "string"
          },
          "assessmentId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "associatedAssessmentIds": {
            "description": "For brokers: other explicitly associated Assessments (each requires its own session).",
            "items": {
              "$ref": "#/components/schemas/EntityId"
            },
            "type": "array"
          },
          "expiresAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "persona": {
            "enum": [
              "insured",
              "broker"
            ],
            "type": "string"
          }
        },
        "required": [
          "accessToken",
          "expiresAt",
          "persona",
          "assessmentId"
        ],
        "type": "object"
      },
      "PortalSessionRequest": {
        "properties": {
          "assessmentRef": {
            "description": "Required for conventional sign-in when the identity is associated with more than one Assessment.",
            "type": "string"
          },
          "linkToken": {
            "description": "Single-use token from the delivered link.",
            "type": "string"
          },
          "method": {
            "enum": [
              "passwordless_link",
              "conventional_signin"
            ],
            "type": "string"
          },
          "signinAssertion": {
            "description": "Assertion from the conventional sign-in provider.",
            "type": "string"
          }
        },
        "required": [
          "method"
        ],
        "type": "object"
      },
      "PortalSigninAssertion": {
        "properties": {
          "signinAssertion": {
            "type": "string"
          }
        },
        "required": [
          "signinAssertion"
        ],
        "type": "object"
      },
      "PortalSigninRequest": {
        "properties": {
          "email": {
            "type": "string"
          },
          "password": {
            "type": "string"
          }
        },
        "required": [
          "email",
          "password"
        ],
        "type": "object"
      },
      "PortalSigninVerifyRequest": {
        "properties": {
          "challengeId": {
            "type": "string"
          },
          "code": {
            "type": "string"
          }
        },
        "required": [
          "challengeId",
          "code"
        ],
        "type": "object"
      },
      "PortalSwitchRequest": {
        "properties": {
          "assessmentId": {
            "$ref": "#/components/schemas/EntityId"
          }
        },
        "required": [
          "assessmentId"
        ],
        "type": "object"
      },
      "PortalUploadFileSpec": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UploadFileSpec"
          },
          {
            "properties": {
              "documentKind": {
                "description": "Read only while lcr_portal_photo_flow is on for the caller. The kind of a document file (422 `invalid_document_kind` on a photo or for another value).",
                "enum": [
                  "invoice",
                  "other"
                ],
                "type": "string"
              },
              "photoAreaId": {
                "description": "Read only while lcr_portal_photo_flow is on for the caller. The photo area (PortalPhotoArea.areaId) this photo answers; it must be one of the recommendation's photo areas and sit on a photo (422 `unknown_photo_area`).",
                "type": "string"
              }
            },
            "type": "object"
          }
        ]
      },
      "PresignedUrl": {
        "properties": {
          "expiresAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "headers": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Headers the client must send (e.g. `Content-Type`, `x-amz-checksum-sha256`) on a PUT.",
            "type": "object"
          },
          "method": {
            "enum": [
              "GET",
              "PUT"
            ],
            "type": "string"
          },
          "url": {
            "format": "uri",
            "type": "string"
          }
        },
        "required": [
          "url",
          "method",
          "expiresAt"
        ],
        "type": "object"
      },
      "Problem": {
        "description": "RFC 9457 Problem Details.",
        "properties": {
          "detail": {
            "type": "string"
          },
          "instance": {
            "description": "Request-scoped id for support correlation (`LCR-FR-091`).",
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "traceId": {
            "type": "string"
          },
          "type": {
            "description": "URI identifying the problem class, e.g. `https://lcr.gsr/problems/version-conflict`.",
            "format": "uri",
            "type": "string"
          }
        },
        "required": [
          "type",
          "title",
          "status"
        ],
        "type": "object"
      },
      "RegionGeometry": {
        "description": "2-D region on an exact image version, in source pixel coordinates. Non-destructive; the original is never mutated.",
        "properties": {
          "bbox": {
            "properties": {
              "height": {
                "type": "number"
              },
              "width": {
                "type": "number"
              },
              "x": {
                "type": "number"
              },
              "y": {
                "type": "number"
              }
            },
            "type": "object"
          },
          "kind": {
            "enum": [
              "whole_image",
              "polygon",
              "bbox",
              "mask_ref"
            ],
            "type": "string"
          },
          "maskDerivativeId": {
            "$ref": "#/components/schemas/EntityId",
            "description": "Not accepted. `mask_ref` is refused; an accepted assisted mask is saved as a `polygon` region with `assisted_mask` provenance (AD-E124-20-04)."
          },
          "polygon": {
            "description": "Closed polygon `[x, y]` pixel vertices for `polygon`.",
            "items": {
              "items": {
                "type": "number"
              },
              "maxItems": 2,
              "minItems": 2,
              "type": "array"
            },
            "type": "array"
          },
          "style": {
            "description": "Highlight style when used as a recipient-visible highlight.",
            "properties": {
              "fill": {
                "type": "string"
              },
              "stroke": {
                "type": "string"
              }
            },
            "type": "object"
          }
        },
        "required": [
          "kind"
        ],
        "type": "object"
      },
      "RequestState": {
        "description": "Dimension 5: item-specific date / alternate-mitigation request state.",
        "enum": [
          "pending",
          "approved",
          "rejected",
          "withdrawn"
        ],
        "type": "string",
        "x-lcr-decided": "DR-089 (was TBD-016)"
      },
      "RoutingPurpose": {
        "description": "Governed communication purposes. The concrete purpose list for the current MGA is open.",
        "enum": [
          "assessment_created",
          "introduction",
          "lcr_delivery",
          "underwriting_condition_failed",
          "underwriting_delivery_notice",
          "compliance_intent_request",
          "reminder",
          "overdue",
          "escalation",
          "item_request",
          "question_reply",
          "evidence_reviewable",
          "more_evidence",
          "interim_resolution",
          "final_closure",
          "support",
          "mention",
          "contradiction",
          "portal_link",
          "portal_mfa_code",
          "portal_recovery_code"
        ],
        "type": "string",
        "x-lcr-decided": "DR-096 (was TBD-024)"
      },
      "RuleProblem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Problem"
          },
          {
            "properties": {
              "effectiveImageRange": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ImageRange"
                  }
                ],
                "description": "The range an `image_count_out_of_range` violation was checked against."
              },
              "violations": {
                "items": {
                  "properties": {
                    "entityId": {
                      "$ref": "#/components/schemas/EntityId"
                    },
                    "guard": {
                      "description": "Stable guard name, e.g. `preview_required`, `image_unconfirmed`, `transition_not_allowed`, `intake_path_healthy`, `risk_improvement_prohibited`.",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "requirement": {
                      "description": "The `LCR-FR-###` the guard enforces.",
                      "type": "string"
                    }
                  },
                  "required": [
                    "guard",
                    "message"
                  ],
                  "type": "object"
                },
                "type": "array"
              }
            },
            "required": [
              "violations"
            ],
            "type": "object"
          }
        ]
      },
      "SupportRequest": {
        "properties": {
          "assessmentId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "createdAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "lastAuditEventId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "notificationId": {
            "$ref": "#/components/schemas/EntityId"
          },
          "state": {
            "enum": [
              "open",
              "resolved"
            ],
            "type": "string"
          },
          "supportRequestId": {
            "$ref": "#/components/schemas/EntityId"
          }
        },
        "required": [
          "supportRequestId",
          "assessmentId",
          "state",
          "createdAt"
        ],
        "type": "object"
      },
      "SupportRequestCreate": {
        "properties": {
          "description": {
            "type": "string"
          },
          "recommendationId": {
            "$ref": "#/components/schemas/EntityId"
          }
        },
        "required": [
          "description"
        ],
        "type": "object"
      },
      "Timestamp": {
        "description": "RFC 3339 UTC.",
        "format": "date-time",
        "type": "string"
      },
      "UnavailableProblem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Problem"
          },
          {
            "properties": {
              "code": {
                "description": "RFC 9457 extension member naming the cause. `database_waking` means the database is resuming from an automatic pause; the same code the risk-app API sends.",
                "examples": [
                  "database_waking"
                ],
                "type": "string"
              }
            },
            "type": "object"
          }
        ]
      },
      "UploadConfirmRequest": {
        "properties": {
          "files": {
            "items": {
              "properties": {
                "clientRef": {
                  "type": "string"
                },
                "etag": {
                  "type": "string"
                }
              },
              "required": [
                "clientRef"
              ],
              "type": "object"
            },
            "minItems": 1,
            "type": "array"
          },
          "note": {
            "type": "string"
          }
        },
        "required": [
          "files"
        ],
        "type": "object"
      },
      "UploadFileSpec": {
        "properties": {
          "byteSize": {
            "minimum": 1,
            "type": "integer"
          },
          "capturedAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "checksumSha256": {
            "type": "string"
          },
          "clientRef": {
            "description": "Client-side key echoed in the session.",
            "type": "string"
          },
          "fileName": {
            "type": "string"
          },
          "mediaType": {
            "type": "string"
          }
        },
        "required": [
          "clientRef",
          "fileName",
          "mediaType",
          "byteSize",
          "checksumSha256"
        ],
        "type": "object"
      },
      "UploadRejection": {
        "properties": {
          "clientRef": {
            "type": "string"
          },
          "code": {
            "enum": [
              "checksum_mismatch",
              "not_uploaded",
              "unsupported_type",
              "oversized",
              "duplicate",
              "policy_rejected"
            ],
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "clientRef",
          "code",
          "message"
        ],
        "type": "object"
      },
      "UploadSession": {
        "properties": {
          "expiresAt": {
            "$ref": "#/components/schemas/Timestamp"
          },
          "files": {
            "items": {
              "properties": {
                "clientRef": {
                  "type": "string"
                },
                "put": {
                  "$ref": "#/components/schemas/PresignedUrl"
                }
              },
              "required": [
                "clientRef",
                "put"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "guidance": {
            "description": "Approved customer/reviewer guidance for the modality (`LCR-FR-063`).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "uploadId": {
            "$ref": "#/components/schemas/EntityId"
          }
        },
        "required": [
          "uploadId",
          "expiresAt",
          "files"
        ],
        "type": "object"
      },
      "ValidationProblem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Problem"
          },
          {
            "properties": {
              "errors": {
                "items": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "field": {
                      "description": "JSON pointer into the request body or a parameter name.",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "field",
                    "code",
                    "message"
                  ],
                  "type": "object"
                },
                "type": "array"
              }
            },
            "required": [
              "errors"
            ],
            "type": "object"
          }
        ]
      }
    },
    "securitySchemes": {
      "portalJwt": {
        "bearerFormat": "JWT",
        "description": "Insured / broker responders via the Localligence portal. Issued by `POST /portal/session` from a\npasswordless single-use link token (or the conventional sign-in fallback) and **scoped to exactly\none Assessment** and one persona (`insured` or `broker` by explicit association, `DR-022`/`DR-032`).\nOnly operations under `/portal/**` accept it (ADR-002). Lifetime, replay, revocation and rate\nlimits per `TBD-006`.\n",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "The operations the homeowner portal calls, and nothing else. Every path is under /lcr/v1/portal/. A portal session token (portalJwt) opens exactly one property for one person, a homeowner or a broker. Examples come from fictional sandbox cases.",
    "title": "LCR homeowner portal API",
    "version": "0.10.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/portal/assessment": {
      "get": {
        "description": "Never conflates delivery, intent, upload/processing, attestation, verification, resolution or closure. Customer vocabulary is mapped from internal states (`TBD-016`).",
        "operationId": "portalGetAssessment",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "assessmentId": "asm_01JZZZZZZZZZZZZZZZZZZZZ001",
                  "deliveredVersion": {
                    "deliveredAt": "2026-11-09T13:38:13.272699Z",
                    "lcrVersionId": "lcv_01JZZZZZZZZZZZZZZZZZZZZ011"
                  },
                  "intent": {
                    "state": "requested"
                  },
                  "needsAttention": [],
                  "pdfAvailable": false,
                  "progress": {
                    "complete": 0,
                    "items": [
                      {
                        "currentDueDate": "2027-11-09",
                        "recipientNumber": 1,
                        "recommendationId": "rec_01JZZZZZZZZZZZZZZZZZZZZ004",
                        "status": "open"
                      }
                    ],
                    "open": 1,
                    "overdue": 0,
                    "total": 1,
                    "underReview": 0
                  },
                  "recommendations": [
                    {
                      "actionText": "FAKE clear brush within five feet",
                      "affectedStructure": "(missing)",
                      "allowedEvidence": [
                        "photo",
                        "document"
                      ],
                      "currentDueDate": "2027-11-09",
                      "evidenceState": "No proof received yet",
                      "guidance": [],
                      "images": [
                        {
                          "access": {
                            "expiresAt": "2030-01-01T00:00:00Z",
                            "method": "GET",
                            "url": "https://media.example.test/raw/ast_01JZZZZZZZZZZZZZZZZZZZZ012.jpg"
                          },
                          "caption": "(missing)"
                        }
                      ],
                      "label": "Recommendation 1",
                      "originalDueDate": "2027-11-09",
                      "recipientNumber": 1,
                      "recommendationId": "rec_01JZZZZZZZZZZZZZZZZZZZZ004",
                      "status": "open",
                      "statusLabel": "Open",
                      "whyItMatters": "FAKE embers ignite brush"
                    }
                  ],
                  "reference": "A-ZPMEPXSK7C",
                  "supportBlocked": false
                },
                "schema": {
                  "$ref": "#/components/schemas/PortalAssessment"
                }
              }
            },
            "description": "Portal assessment view."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "The recipient view of the delivered LCR: recommendations, numbers, dates, evidence and customer-facing status",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-058",
          "LCR-FR-059",
          "LCR-FR-120"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/assessment/intent": {
      "get": {
        "operationId": "portalGetIntent",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "deadline": "2026-11-23",
                  "state": "requested",
                  "versions": []
                },
                "schema": {
                  "$ref": "#/components/schemas/IntentResponse"
                }
              }
            },
            "description": "Intent state."
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "The current LCR-level compliance-intent response and its versions",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-060"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      },
      "put": {
        "description": "Three semantic choices. A new submission creates a new version; no intent outcome acknowledges receipt, completes an item, gates questions/requests/proof or cancels a policy.",
        "operationId": "portalPutIntent",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "choice": "comply_by_current_dates",
                "comment": "We will do all of it by the dates."
              },
              "schema": {
                "$ref": "#/components/schemas/IntentResponsePut"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "current": {
                    "choice": "comply_by_current_dates",
                    "comment": "We will do all of it by the dates.",
                    "lcrVersionId": "lcv_01JZZZZZZZZZZZZZZZZZZZZ011",
                    "namedRequestIds": [],
                    "respondedAt": "2026-11-09T13:38:13.768146Z",
                    "responder": {
                      "actorId": "pid_01JZZZZZZZZZZZZZZZZZZZZ005",
                      "actorType": "insured"
                    }
                  },
                  "deadline": "2026-11-23",
                  "lastAuditEventId": "evt_01JZZZZZZZZZZZZZZZZZZZZ015",
                  "state": "answered",
                  "versions": [
                    {
                      "choice": "comply_by_current_dates",
                      "disposition": "valid",
                      "respondedAt": "2026-11-09T13:38:13.768146Z",
                      "responder": {
                        "actorId": "pid_01JZZZZZZZZZZZZZZZZZZZZ005",
                        "actorType": "insured"
                      },
                      "version": 1
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/IntentResponse"
                }
              }
            },
            "description": "Recorded intent version.",
            "headers": {
              "LCR-Audit-Event-Id": {
                "$ref": "#/components/headers/LCR-Audit-Event-Id"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/RuleViolation"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Submit or correct the one LCR-level compliance-intent response",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-089 (was TBD-016)",
        "x-lcr-requirements": [
          "LCR-FR-060",
          "LCR-FR-069"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/assessment/progress": {
      "get": {
        "operationId": "portalGetProgress",
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "schema": {
              "$ref": "#/components/schemas/CustomerFacingStatus"
            }
          },
          {
            "in": "query",
            "name": "dueBefore",
            "schema": {
              "format": "date",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "complete": 0,
                  "items": [
                    {
                      "currentDueDate": "2027-11-09",
                      "recipientNumber": 1,
                      "recommendationId": "rec_01JZZZZZZZZZZZZZZZZZZZZ004",
                      "status": "open"
                    }
                  ],
                  "open": 1,
                  "overdue": 0,
                  "total": 1,
                  "underReview": 0
                },
                "schema": {
                  "$ref": "#/components/schemas/PortalProgress"
                }
              }
            },
            "description": "Progress rollup and filtered items."
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Assessment-level mitigation/completion progress with item filters by status and current due date",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-059"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/assessment/report": {
      "get": {
        "operationId": "portalGetReportPdf",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PresignedUrl"
                }
              }
            },
            "description": "Presigned URL."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Presigned download of the rendered PDF for the delivered version (when the program's PDF switch is on)",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-051",
          "LCR-FR-117"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/associations": {
      "get": {
        "description": "A broker lists the Assessments its live associations grant, most recently associated first. While lcr_portal_case_details is off for the caller an insured is refused with the same 403 the role guard gives a persona it does not admit, and items carry only assessmentId and reference. With it on, an insured lists every case it holds an issued link for, newest link first (the set portalSwitchAssessment accepts), and every item also carries propertyAddress and customerStatus.",
        "operationId": "portalListAssociatedAssessments",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalAssociatedAssessments"
                }
              }
            },
            "description": "Associated Assessments, most recently associated first."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "The broker's explicitly associated Assessments, and with lcr_portal_case_details on an insured's own cases",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-022",
        "x-lcr-requirements": [
          "LCR-FR-068"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/evidence/uploads/{uploadId}/confirm": {
      "parameters": [
        {
          "$ref": "#/components/parameters/UploadId"
        }
      ],
      "post": {
        "operationId": "portalConfirmEvidenceUpload",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UploadConfirmRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "rejected": {
                      "items": {
                        "$ref": "#/components/schemas/UploadRejection"
                      },
                      "type": "array"
                    },
                    "submissions": {
                      "items": {
                        "$ref": "#/components/schemas/EvidenceSubmission"
                      },
                      "type": "array"
                    }
                  },
                  "required": [
                    "submissions"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Submissions created; partial failures listed.",
            "headers": {
              "LCR-Audit-Event-Id": {
                "$ref": "#/components/headers/LCR-Audit-Event-Id"
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Confirm the batch; creates one evidence submission per accepted file in `received` state",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-062",
          "LCR-FR-102",
          "LCR-FR-069"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/links/reissue": {
      "post": {
        "description": "Always `202` with one fixed body, for unknown, forged, throttled and successful inputs alike; a new link goes to the identity of a genuine link under the same limits.",
        "operationId": "requestPortalLinkReissue",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalLinkReissueRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalAccepted"
                }
              }
            },
            "description": "Accepted."
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [],
        "summary": "Self-service \"send me a new link\" from an expired or used link screen",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-079",
        "x-lcr-requirements": [
          "LCR-FR-056",
          "LCR-NFR-002"
        ]
      }
    },
    "/portal/questions": {
      "get": {
        "operationId": "portalListQuestions",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "items": [
                    {
                      "assessmentId": "asm_01JZZZZZZZZZZZZZZZZZZZZ001",
                      "attachments": [],
                      "body": "Does the vent screen need to be metal, or is plastic mesh fine?",
                      "channel": "portal",
                      "deliveryOutcome": {
                        "destinations": [],
                        "state": "failed"
                      },
                      "direction": "inbound",
                      "kind": "question",
                      "messageId": "msg_01JZZZZZZZZZZZZZZZZZZZZ002",
                      "notificationId": "ntf_01JZZZZZZZZZZZZZZZZZZZZ003",
                      "recipients": [
                        {
                          "actorType": "internal_user",
                          "persona": "loss_control"
                        }
                      ],
                      "recommendationId": "rec_01JZZZZZZZZZZZZZZZZZZZZ004",
                      "sender": {
                        "actorId": "pid_01JZZZZZZZZZZZZZZZZZZZZ005",
                        "actorType": "insured",
                        "persona": "insured"
                      },
                      "sentAt": "2026-11-09T13:38:14.008518Z",
                      "threadId": "asm_01JZZZZZZZZZZZZZZZZZZZZ001"
                    }
                  ],
                  "page": {
                    "nextCursor": null,
                    "pageSize": 1
                  }
                },
                "schema": {
                  "properties": {
                    "items": {
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      },
                      "type": "array"
                    },
                    "page": {
                      "$ref": "#/components/schemas/PageMeta"
                    }
                  },
                  "required": [
                    "items",
                    "page"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Thread."
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Questions and replies visible to this responder (Assessment- or recommendation-level)",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-064"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      },
      "post": {
        "operationId": "portalAskQuestion",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "body": "Does the vent screen need to be metal, or is plastic mesh fine?",
                "recommendationId": "rec_01JZZZZZZZZZZZZZZZZZZZZ004"
              },
              "schema": {
                "$ref": "#/components/schemas/MessageCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "assessmentId": "asm_01JZZZZZZZZZZZZZZZZZZZZ001",
                  "attachments": [],
                  "body": "Does the vent screen need to be metal, or is plastic mesh fine?",
                  "channel": "portal",
                  "deliveryOutcome": {
                    "destinations": [],
                    "state": "failed"
                  },
                  "direction": "inbound",
                  "kind": "question",
                  "messageId": "msg_01JZZZZZZZZZZZZZZZZZZZZ002",
                  "notificationId": "ntf_01JZZZZZZZZZZZZZZZZZZZZ003",
                  "recipients": [
                    {
                      "actorType": "internal_user",
                      "persona": "loss_control"
                    }
                  ],
                  "recommendationId": "rec_01JZZZZZZZZZZZZZZZZZZZZ004",
                  "sender": {
                    "actorId": "pid_01JZZZZZZZZZZZZZZZZZZZZ005",
                    "actorType": "insured",
                    "persona": "insured"
                  },
                  "sentAt": "2026-11-09T13:38:14.008518Z",
                  "threadId": "asm_01JZZZZZZZZZZZZZZZZZZZZ001"
                },
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            },
            "description": "Question recorded in the shared communication record.",
            "headers": {
              "LCR-Audit-Event-Id": {
                "$ref": "#/components/headers/LCR-Audit-Event-Id"
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Ask a question at Assessment or recommendation level (not gated by intent)",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-064",
          "LCR-FR-011"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/questions/read": {
      "post": {
        "description": "Behind `lcr_portal_case_details`: 404 while the flag is off for the caller. Records a read mark for the session's identity on one question thread of the session's Assessment. Staff messages sent after the latest mark count in PortalAssessment.unreadQuestions. A thread on another Assessment, or none, is refused with 422 and nothing is written.",
        "operationId": "portalMarkQuestionsRead",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalQuestionsReadCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalQuestionsRead"
                }
              }
            },
            "description": "The read mark, and the identity's unread count after it."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Mark a question thread read for the signed-in responder",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-064"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/recommendations/{recommendationId}/answer": {
      "get": {
        "description": "Behind `lcr_portal_per_action_answers`: 404 while the flag is off for the caller (after the role guard's 401 and 403). The latest answer on the action counts; an action nobody has answered reads `not_answered` with its agree-by deadline (DR-150, DR-151). An action the portal does not show (not delivered, withdrawn or superseded) answers 404.",
        "operationId": "portalGetItemAnswer",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalItemAnswer"
                }
              }
            },
            "description": "The action's current answer."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "The homeowner's current answer on one delivered action",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-150",
        "x-lcr-requirements": [
          "LCR-FR-060"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/RecommendationId"
        }
      ],
      "put": {
        "description": "Behind `lcr_portal_per_action_answers`: 404 while the flag is off for the caller (after the role guard's 401 and 403). Every answer is kept and the latest counts (DR-150). Only a delivered action that is still open takes an answer: a resolved one is 409 `not_answerable`, and one the portal does not show is 404. `will_not_complete` needs a reason (422 with the guard `reason_required`). `need_change` needs `change` with `requestedDate`, `alternateOutcome` or both (422 `change_required`) and creates one item request in the same transaction: more time is a `date_adjustment`, a different approach an `alternate_mitigation`, both one `date_and_alternate`; while a request on the action is pending it is refused 409 `open_request_exists` and nothing is written. `change` with another answer is 422 `change_not_allowed`. An answer after the agree-by deadline (30 days after the action's delivery, at 17:00 America/Chicago, DR-151) is recorded with `late: true`, never refused. No answer verifies or resolves the action, gates proof (DR-152), changes a due date or cancels a policy.",
        "operationId": "portalPutItemAnswer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalItemAnswerPut"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalItemAnswer"
                }
              }
            },
            "description": "The action's current answer, which is this one.",
            "headers": {
              "LCR-Audit-Event-Id": {
                "$ref": "#/components/headers/LCR-Audit-Event-Id"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/RuleViolation"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Answer one delivered action - I'll do this, I need a change, or I will not complete this",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-150, DR-151",
        "x-lcr-requirements": [
          "LCR-FR-060",
          "LCR-FR-061",
          "LCR-FR-069"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/recommendations/{recommendationId}/attestations": {
      "parameters": [
        {
          "$ref": "#/components/parameters/RecommendationId"
        }
      ],
      "post": {
        "description": "Records the exact statement/question, allowed and selected response, responder authority and catalog/rule version. Never auto-resolves; always enters human verification. Rejected for recommendations requiring photo/document/reinspection.",
        "operationId": "portalSubmitAttestation",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AttestationCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvidenceSubmission"
                }
              }
            },
            "description": "Attestation submission.",
            "headers": {
              "LCR-Audit-Event-Id": {
                "$ref": "#/components/headers/LCR-Audit-Event-Id"
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/RuleViolation"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Submit a controlled structured attestation where the published catalog permits it",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-125",
          "LCR-FR-069"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/recommendations/{recommendationId}/evidence": {
      "get": {
        "description": "Behind `lcr_portal_photo_flow`: 404 while the flag is off for the caller. Every file the insured or the broker sent for this recommendation, newest first, including files refused at intake and files cited from another recommendation; attestations are not files and are not listed. `status` is the file's own status in the customer vocabulary. A recommendation the portal does not show answers 404.",
        "operationId": "portalListEvidence",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalEvidenceList"
                }
              }
            },
            "description": "The files sent for the recommendation."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "What was sent for one recommendation, when and by whom",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-062",
          "LCR-FR-102"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/RecommendationId"
        }
      ]
    },
    "/portal/recommendations/{recommendationId}/evidence/uploads": {
      "parameters": [
        {
          "$ref": "#/components/parameters/RecommendationId"
        }
      ],
      "post": {
        "description": "Files go straight to the quarantine prefix; nothing is viewable or reviewable until validation and security processing pass (`LCR-NFR-022`). Upload is neither intent nor verified completion. Includes angle/aerial guidance for the modality.",
        "operationId": "portalInitiateEvidenceUpload",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "files": [
                  {
                    "byteSize": 32,
                    "checksumSha256": "6d0da58fc4facb8db556db54dbb443f2307534a39ada9190b8110c80003e4195",
                    "clientRef": "front-vent-after",
                    "fileName": "front-vent-after.jpg",
                    "mediaType": "image/jpeg"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/EvidenceUploadInitRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "expiresAt": "2026-11-09T13:53:13.865586Z",
                  "files": [
                    {
                      "clientRef": "front-vent-after",
                      "put": {
                        "expiresAt": "2026-11-09T13:53:13.865586Z",
                        "headers": {
                          "Content-Type": "image/jpeg",
                          "x-amz-checksum-sha256": "bQ2lj8T6y421VttU27RD8jB1NKOa2pGQuBEMgAA+QZU="
                        },
                        "method": "PUT",
                        "url": "https://uploads.example.test/quarantine/asm_01JZZZZZZZZZZZZZZZZZZZZ001/evs_01JZZZZZZZZZZZZZZZZZZZZ013/6d0da58fc4fa"
                      }
                    }
                  ],
                  "guidance": [],
                  "limits": {
                    "allowedMediaTypes": [
                      "application/pdf",
                      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                      "image/jpeg",
                      "image/png"
                    ],
                    "maxBytesPerFile": 26214400,
                    "maxBytesTotal": 262144000,
                    "maxFiles": 10
                  },
                  "recommendationId": "rec_01JZZZZZZZZZZZZZZZZZZZZ004",
                  "rejected": [],
                  "uploadId": "evb_01JZZZZZZZZZZZZZZZZZZZZ014"
                },
                "schema": {
                  "$ref": "#/components/schemas/EvidenceUploadSession"
                }
              }
            },
            "description": "Upload session with one presigned PUT per accepted file and guidance."
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/RuleViolation"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Initiate a proof photo / PDF / Word upload batch against one recommendation: returns presigned PUTs",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-099 (was TBD-029)",
        "x-lcr-requirements": [
          "LCR-FR-062",
          "LCR-FR-102",
          "LCR-FR-063",
          "LCR-NFR-022"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/recommendations/{recommendationId}/requests": {
      "get": {
        "description": "Behind `lcr_portal_case_details`: 404 while the flag is off for the caller. Every date or alternate-mitigation request on this recommendation, newest first: its kind, the requested date and outcome, the homeowner's reason, its state, when loss control decided it and what was granted. A value nobody recorded is null. A recommendation the portal does not show answers 404.",
        "operationId": "portalListItemRequests",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalItemRequestList"
                }
              }
            },
            "description": "The requests on the recommendation."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Every request on one recommendation, with its decision time and granted values",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-061"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/RecommendationId"
        }
      ],
      "post": {
        "operationId": "portalCreateItemRequest",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "kind": "date_adjustment",
                "reason": "We need two more weeks to book a contractor.",
                "requestedDate": "2027-11-23"
              },
              "schema": {
                "$ref": "#/components/schemas/ItemRequestCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "assessmentId": "asm_01JZZZZZZZZZZZZZZZZZZZZ001",
                  "createdAt": "2026-11-09T13:38:13.686441Z",
                  "createdBy": {
                    "actorId": "pid_01JZZZZZZZZZZZZZZZZZZZZ005",
                    "actorType": "insured"
                  },
                  "kind": "date_adjustment",
                  "lastAuditEventId": "evt_01JZZZZZZZZZZZZZZZZZZZZ006",
                  "reason": "We need two more weeks to book a contractor.",
                  "recommendationId": "rec_01JZZZZZZZZZZZZZZZZZZZZ004",
                  "recommendationVersion": 4,
                  "requestId": "req_01JZZZZZZZZZZZZZZZZZZZZ007",
                  "requestedAt": "2026-11-09T13:38:13.686441Z",
                  "requestedBy": {
                    "actorId": "pid_01JZZZZZZZZZZZZZZZZZZZZ005",
                    "actorType": "insured"
                  },
                  "requestedDate": "2027-11-23",
                  "routing": {
                    "destinations": [
                      {
                        "audience": "loss_control",
                        "channel": "email",
                        "destinationId": "dst_01JZZZZZZZZZZZZZZZZZZZZ008",
                        "notice": "submitted",
                        "notificationId": "ntf_01JZZZZZZZZZZZZZZZZZZZZ009",
                        "outcome": "pending",
                        "required": true,
                        "role": "to"
                      }
                    ],
                    "notificationId": "ntf_01JZZZZZZZZZZZZZZZZZZZZ009",
                    "resolvedFrom": "client_purpose",
                    "routingProfileId": "def_01JZZZZZZZZZZZZZZZZZZZZ010",
                    "routingProfileVersion": 1,
                    "valid": true
                  },
                  "state": "pending",
                  "updatedAt": "2026-11-09T13:38:13.686441Z",
                  "updatedBy": {
                    "actorId": "pid_01JZZZZZZZZZZZZZZZZZZZZ005",
                    "actorType": "insured"
                  },
                  "version": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/ItemRequest"
                }
              }
            },
            "description": "Pending request routed to the approved authority.",
            "headers": {
              "LCR-Audit-Event-Id": {
                "$ref": "#/components/headers/LCR-Audit-Event-Id"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/RuleViolation"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Request a date adjustment or allowed alternate mitigation for one recommendation",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-061",
          "LCR-FR-069"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/recovery": {
      "post": {
        "description": "Always `202` with a challenge id, so the response never discloses whether an identity exists.",
        "operationId": "startPortalRecovery",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalRecoveryRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalChallenge"
                }
              }
            },
            "description": "Challenge started."
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [],
        "summary": "Start password recovery (or first password set-up); a code is emailed on a real match",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-079",
        "x-lcr-requirements": [
          "LCR-FR-057",
          "LCR-NFR-002"
        ]
      }
    },
    "/portal/recovery/complete": {
      "post": {
        "operationId": "completePortalRecovery",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalRecoveryCompleteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Password set."
          },
          "401": {
            "$ref": "#/components/responses/PortalAccessRefused"
          },
          "422": {
            "$ref": "#/components/responses/RuleViolation"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [],
        "summary": "Complete recovery with the emailed code and a new password; every open session of the identity ends",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-079",
        "x-lcr-requirements": [
          "LCR-FR-057",
          "LCR-NFR-002"
        ]
      }
    },
    "/portal/session": {
      "delete": {
        "operationId": "endPortalSession",
        "responses": {
          "204": {
            "description": "Signed out."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Sign out; the session's still-valid portal JWT is refused from now on",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-079",
        "x-lcr-requirements": [
          "LCR-FR-057",
          "LCR-NFR-002"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      },
      "post": {
        "description": "Single-use, short-lived. Expired, revoked, replayed, forwarded/wrong-association tokens fail without revealing data. Records the successful-access metric for the persona and access method. Every refusal is one uniform `401` (code `portal_access_refused`, no ids). A repeated Idempotency-Key after a success is evaluated again and refused as used, so the JWT is never returned twice. Without `assessmentRef` a conventional sign-in session is scoped to the most recently associated Assessment and `associatedAssessmentIds` lists the others (E125-01).",
        "operationId": "exchangePortalLink",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "linkToken": "LINK-TOKEN-FROM-THE-EMAILED-LINK",
                "method": "passwordless_link"
              },
              "schema": {
                "$ref": "#/components/schemas/PortalSessionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "accessToken": "SESSION-TOKEN-FROM-POST-PORTAL-SESSION",
                  "assessmentId": "asm_01JZZZZZZZZZZZZZZZZZZZZ001",
                  "associatedAssessmentIds": [],
                  "expiresAt": "2026-11-09T14:08:13.576388Z",
                  "persona": "insured"
                },
                "schema": {
                  "$ref": "#/components/schemas/PortalSession"
                }
              }
            },
            "description": "Portal session."
          },
          "401": {
            "$ref": "#/components/responses/PortalAccessRefused"
          },
          "429": {
            "$ref": "#/components/responses/Throttled"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [],
        "summary": "Exchange a passwordless link token (or conventional sign-in assertion) for a portal JWT scoped to one Assessment",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-079 (was TBD-006)",
        "x-lcr-requirements": [
          "LCR-FR-056",
          "LCR-FR-057",
          "LCR-FR-068",
          "LCR-NFR-002"
        ]
      }
    },
    "/portal/session/switch": {
      "post": {
        "description": "The target must be in the identity's current Assessment set (insured by issued link, broker by explicit association). The current session is not ended.",
        "operationId": "portalSwitchAssessment",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalSwitchRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalSession"
                }
              }
            },
            "description": "Portal session for the target Assessment."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "A new session for another Assessment the identity is associated with",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-022, DR-079",
        "x-lcr-requirements": [
          "LCR-FR-057",
          "LCR-FR-068"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    },
    "/portal/signin": {
      "post": {
        "description": "Always `202` with a challenge id, whether or not the email or password matched, so the response never discloses whether an identity exists.",
        "operationId": "startPortalSignin",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalSigninRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalChallenge"
                }
              }
            },
            "description": "Challenge started."
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [],
        "summary": "Conventional sign-in fallback, step 1 - email and password; a code is emailed on a real match",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-079",
        "x-lcr-requirements": [
          "LCR-FR-057",
          "LCR-NFR-002"
        ]
      }
    },
    "/portal/signin/verify": {
      "post": {
        "description": "Five wrong codes consume the challenge. Every refusal is the uniform `401`.",
        "operationId": "verifyPortalSignin",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortalSigninVerifyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalSigninAssertion"
                }
              }
            },
            "description": "Sign-in assertion for `exchangePortalLink` (method `conventional_signin`), valid five minutes."
          },
          "401": {
            "$ref": "#/components/responses/PortalAccessRefused"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [],
        "summary": "Conventional sign-in fallback, step 2 - the emailed code; returns a single-use sign-in assertion",
        "tags": [
          "Compliance"
        ],
        "x-lcr-decided": "DR-079",
        "x-lcr-requirements": [
          "LCR-FR-057",
          "LCR-NFR-002"
        ]
      }
    },
    "/portal/support-requests": {
      "post": {
        "operationId": "portalRequestSupport",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "description": "The upload button does nothing on my phone."
              },
              "schema": {
                "$ref": "#/components/schemas/SupportRequestCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "assessmentId": "asm_01JZZZZZZZZZZZZZZZZZZZZ001",
                  "createdAt": "2026-11-09T13:38:13.901909Z",
                  "lastAuditEventId": "evt_01JZZZZZZZZZZZZZZZZZZZZ016",
                  "notificationId": "ntf_01JZZZZZZZZZZZZZZZZZZZZ017",
                  "state": "open",
                  "supportRequestId": "spr_01JZZZZZZZZZZZZZZZZZZZZ018"
                },
                "schema": {
                  "$ref": "#/components/schemas/SupportRequest"
                }
              }
            },
            "description": "Linked, owned support record.",
            "headers": {
              "LCR-Audit-Event-Id": {
                "$ref": "#/components/headers/LCR-Audit-Event-Id"
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "security": [
          {
            "portalJwt": []
          }
        ],
        "summary": "Request technical help: routed to the shared support destination; marks the Assessment customer-blocked",
        "tags": [
          "Compliance"
        ],
        "x-lcr-requirements": [
          "LCR-FR-067"
        ],
        "x-lcr-roles": [
          "insured",
          "broker"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "lcr-api behind the existing risk-app ALB under the `/lcr` path prefix (ADR-012)",
      "url": "https://{host}/lcr/v1",
      "variables": {
        "host": {
          "default": "uw-rs-dc-app.internal.example",
          "description": "Environment host (dev / qa / prod). The portal reaches the same host through the WAF-fronted external listener."
        }
      }
    }
  ],
  "tags": [
    {
      "description": "Portal operations: the explicit external subset (ADR-002). Every request is scoped to the single Assessment named by the portal token.",
      "name": "Compliance"
    }
  ]
}
