CORA integration — overview

How the CORA document-review agent works with Fortress

CORA reviews applicant documents for affordable-housing move-in applications. Fortress stays the system of record: it decides what is required, CORA decides whether a submitted document satisfies it, and Fortress staff have the final word.

Unit of work

The household’s primary move-in application (applicationId). Every route and event is keyed to it. Read the application itself with the platform GET /applications/{applicationId}, its household members with GET /households/{householdId} and GET /customers filtered by household, and its requirement slots with GET /applications/{applicationId}/requirements.

Requirement slots

A requirement slot is one thing the application needs — a document, form or verification — for one household member (or household-level). Each slot carries one status:

StatusSet byMeaning
WAITING_ON_APPLICANTSystem / CORA / StaffApplicant must act. waitingReason: NOT_UPLOADED, REPLACEMENT_REQUESTED, CLARIFICATION_REQUESTED, RESIGNATURE_REQUESTED
NOT_YET_REVIEWEDSystemUpload arrived while CORA was off for this household
IN_REVIEWSystemUpload dispatched to CORA (requirement.upload_received). CORA never sets this
APPROVED_BY_CORACORANot final
NEEDS_COMPLIANCE_REVIEWCORARequires an ESCALATION reason code
CORRECTION_IN_PROGRESSCORAPermitted application correction underway
WAITING_ON_SITE_TEAMStaffA site task is open for the slot. No agent route creates one in this version
APPROVED_BY_COMPLIANCEStaff onlyAuthoritative. Any agent write → 409
CANCELEDStaff / rulesRequirement no longer needed. A deleted document never cancels a slot — it reopens it

WAITING_ON_APPLICANT is who acts; NOT_UPLOADED is why (and also the uploadState value). A slot can be WAITING_ON_APPLICANT with a file still attached — e.g. REPLACEMENT_REQUESTED.

The one agent write

CORA changes a slot only by posting a finding (POST /applications/{applicationId}/requirements/{requirementId}/findings). The outcome is the transition: APPROVE, REQUEST_REPLACEMENT, REQUEST_CORRECTION, ROUTE_TO_COMPLIANCE. There is no agent status PATCH.

Conventions

  • requirementVersion on every requirement write — 409 if stale.
  • Any field ending in Id is a UUID. Reason codes are strings (reasonCode, overrideReasonCode).
  • Cross-tenant → 404, never 403. Household in Manual Compliance Review → 409 PAUSED_FOR_COMPLIANCE_REVIEW.
  • Webhooks are the trigger; the API is the source of truth. Never poll for changes.

Read-only for agents

CORA configuration (GET /properties/{propertyId}/coraConfiguration, GET /households/{householdId}/coraConfiguration) follows the same contract as the Leslie configuration and is GET only on the partner surface, as is the cadence policy (GET /properties/{propertyId}/agents/cora/cadencePolicy). Enable/disable, settings and outreach cadence are managed by Fortress staff and Product; CORA reads them when starting work and re-reads on agent.property_state_changed. Whether CORA may act = global switch (tracked from agent.global_state_changed) and property isEnabled and household effectiveEnabled and no open Manual Compliance Review. Slot creation is Fortress’s: there is no agent route to add a supplemental requirement or to trigger a rules recompute — corrections run the recompute automatically, and anything else goes to Compliance via ROUTE_TO_COMPLIANCE. Slot status transitions (PATCH …/requirements/{requirementId}/status) are likewise staff-only — an agent reaches IN_REVIEW by receiving requirement.upload_received and leaves it only through a finding.

Guardrails Fortress enforces

Agent writeResponse
Requirement write without the current requirementVersion409
Any agent status PATCH (into or out of IN_REVIEW)400
ROUTE_TO_COMPLIANCE without an ESCALATION code, REQUEST_REPLACEMENT without a REPLACEMENT code400
Write on APPROVED_BY_COMPLIANCE409
Write while the household is in Manual Compliance Review409 PAUSED_FOR_COMPLIANCE_REVIEW
Correction to a field that is not correctable through the API400
Transcript bodies, message bodies or raw application values in any payload400