CORA integration — review flow

From activation to approval

Sequence

Agent transitions — what a finding does

Every agent exit from IN_REVIEW is a finding. The outcome is the transition. There is no agent status PATCH.

FromToCall
IN_REVIEWAPPROVED_BY_CORAfinding outcome: APPROVE
IN_REVIEWWAITING_ON_APPLICANT / REPLACEMENT_REQUESTEDfinding outcome: REQUEST_REPLACEMENT + REPLACEMENT reason code
IN_REVIEWCORRECTION_IN_PROGRESSfinding outcome: REQUEST_CORRECTION, then POST /applications/{applicationId}/corrections
IN_REVIEWNEEDS_COMPLIANCE_REVIEWfinding outcome: ROUTE_TO_COMPLIANCE + ESCALATION reason code (400 without)

Fortress-driven transitions — what CORA observes

TriggerFrom → ToEvent CORA receives
Rules create a slot— → WAITING_ON_APPLICANT / NOT_UPLOADEDrequirement.added
Upload, CORA onWAITING_ON_APPLICANTIN_REVIEWrequirement.upload_received
Upload, CORA offWAITING_ON_APPLICANTNOT_YET_REVIEWEDnone
CORA comes back on / review closesNOT_YET_REVIEWEDIN_REVIEW, one slot at a timerequirement.upload_received per slot (re-dispatch)
Current document deletedany (incl. APPROVED_BY_COMPLIANCE) → WAITING_ON_APPLICANT / NOT_UPLOADEDrequirement.upload_removed
Correction applied, rules okCORRECTION_IN_PROGRESSNOT_YET_REVIEWEDrequirement.changed
Correction applied, needs humanCORRECTION_IN_PROGRESSNEEDS_COMPLIANCE_REVIEWrequirement.needs_compliance_review
Staff approvesany → APPROVED_BY_COMPLIANCErequirement.satisfied {approvedBy: COMPLIANCE, sessionId}
Staff sends backany → WAITING_ON_APPLICANT (any waitingReason)requirement.replacement_requested or requirement.reviewer_finding_submitted
Staff cancels / rules recomputeany → CANCELED; new slots; requirementVersion++requirement.canceled / .added / .changed

Edge cases (at-least-once, unordered delivery)

SituationCORA action
Duplicate eventDedupe on eventId. A repeated finding is rejected — re-read the slot
upload_removed mid-reviewCancel work, discard results. A late finding is rejected
upload_removed arrives before upload_receivedProcess by slot state, not event order; re-read the slot on conflict
Slot stuck IN_REVIEW (no finding posted)Raise a STUCK_OR_FAILED_WORKFLOW alert (POST .../agents/cora/alerts); staff may transition the slot
Staff decided first (requirement.satisfied {actorType: USER}, your finding → 409)Abandon the finding, stop outreach for that slot
application.updated / requirement.changed after a verdictRe-read the application and the slot; re-review against the new requirementVersion
Household pausedQueue; resume on compliance_review.closed, only unresolved slots
Kill switch off / onStop safely; on re-enable Fortress re-dispatches NOT_YET_REVIEWED slots as upload_received

Cadence

Fortress stores the outreach policy per property (GET /properties/{propertyId}/agents/cora/cadencePolicy); CORA executes it. A SITE_TASK touchpoint marks where staff escalation would happen; creating site tasks through the API is not part of this version. One clock per household, started at application.activated, reset on any applicant reply or upload. Touchpoints are placed by offset {days?, hours?}; a touchpoint inside quietHours is deferred to quietHours.end. Treat the policy as data — never hard-code it.