> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.fortresstech.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.fortresstech.io/_mcp/server.

# Release Notes

Stay up to date with the latest database view and schema changes in the Fortress API.

## November 2026

### CORA routes published - November 4, 2026

#### API Reference

**Now served** — these routes moved from *Defined, not yet published* to served. Their contracts are unchanged:
authenticate with the `x-api-key` header, and the key needs the permission each route names.

* **Compliance Documents** — [`GET /v1/compliance-documents/manifest`](/api-reference/compliance-documents/get-compliance-documents-manifest)
  and [`GET /v1/compliance-documents/applications/{applicationId}/bundle`](/api-reference/compliance-documents/get-application-bundle).
* **Documents** — [`POST /v1/documents/{documentId}/results`](/api-reference/documents/post-document-results).
  `409 DOCUMENT_REMOVED` is active: a write still in flight for a document a Fortress user removed is refused.
* **Applications** — [`POST /v1/applications/{applicationId}/cancel`](/api-reference/applications/cancel-application).
* **Sensitive** — [`GET /v1/sensitive/applications/{applicationId}`](/api-reference/sensitive/get-application-sensitive-values).
* **Webhooks** — [`POST /v1/webhooks/trigger`](/api-reference/webhooks/trigger-webhook-event) is served through the
  public gateway; the `webhooks: create` permission is all it needs.

**New Endpoints**

* **[`GET /applications/{applicationId}/full`](/api-reference/applications/get-application-full)** — one
  application in one read, from the Fortress database: application, household, every live applicant with
  customer, addresses, jobs, spouse, minor record, `incomes[]` and `assets[]` lines and deductions, only the
  values the CORA taxonomy compares documents against. Needs `applications: full-read`; the `customers`,
  `incomes`, `assets`, `deductions` and `sensitive` grants add their sections.

## October 2026

### Sensitive Values (Defined, not yet published) - October 29, 2026

#### API Reference

**New Endpoints**

* **[`GET /sensitive/applications/{applicationId}`](/api-reference/sensitive/get-application-sensitive-values)** —
  the full Social Security, passport and alien registration numbers of every person on an application, and the
  institution and account number of every bank account `/assets` returns, each encrypted for the calling
  partner (AES-256-GCM; see [Sensitive values](/ai-team-members/cora/sensitive-values)). Needs an API key enabled for
  sensitive values and a secret from Fortress (`403` otherwise). Not served until it moves to Available.

### Webhook & API Updates - October 27, 2026

#### Webhooks

**New Events** (defined, not yet delivered: build against the trigger, real deliveries start when the publisher ships)

* **[`subscription.created`](/webhooks/webhook-events/webhooks/subscription-created)**, **[`subscription.updated`](/webhooks/webhook-events/webhooks/subscription-updated)** and **[`subscription.ended`](/webhooks/webhook-events/webhooks/subscription-ended)** — resident subscription changes. Each payload carries `provider` and `serviceType`. Act only on rows whose `provider` is your own slug.
* **[`customer.updated`](/webhooks/webhook-events/webhooks/customer-updated)** and **[`lease.updated`](/webhooks/webhook-events/webhooks/lease-updated)** — a watched field changed on a customer or lease with a live subscription. The payload names the changed fields and the subscription ids. Read the new values from the API.
* **Webhook trigger** — accepts `partnerKey` (required for the five new events, default `cora` for CORA events) and the five new event types, and marks every sample with `"test": true` in the signed body and the header `X-Fortress-Test-Event: true`. Still defined, not served through the public gateway.

#### API Reference

**New Endpoints**

* **Subscriptions** (defined, not yet published) — [`GET /subscriptions`](/api-reference/subscriptions/list-subscriptions) and [`GET /subscriptions/{subscriptionId}`](/api-reference/subscriptions/read-subscription) return resident subscription records.

**Guidance**

* **Rate limit** — keep each API key under 600 requests a minute. Fortress may enforce it with `429 Too Many Requests`. See [Rate limits and polling](/api-reference#rate-limits-and-polling) in the API overview.

### API Updates - October 26, 2026

#### API Reference

**New Fields**

* **[`GET /customers`](/api-reference/customers/list-customers)** and
  **[`GET /customers/{customerId}`](/api-reference/customers/read-customer)** — person details from the
  rental application, all nullable and omitted when not on file: `passportNumberLastFour`, `passportCountry`
  (country name), `alienRegistrationNumberLastFour`, `citizenshipStatus` (HUD member citizenship code),
  `maritalStatus`, `spouseName` and `studentType`. Passport and alien registration numbers are returned as
  their last 4 characters only. Existing fields are unchanged.
* **[`GET /customers`](/api-reference/customers/list-customers)** and
  **[`GET /customers/{customerId}`](/api-reference/customers/read-customer)** — current address and lender
  from the rental application, all nullable and omitted when not on file: `currentAddressStreet`,
  `currentAddressStreet2`, `currentAddressCity`, `currentAddressState` (two-letter code),
  `currentAddressZipCode` and `lenderName` (landlord or mortgage lender for that address). Existing fields
  are unchanged.

**New Endpoints**

* **[`GET /applications/{applicationId}/minors`](/api-reference/applications/list-application-minors)** and
  **[`GET /applications/{applicationId}/minors/{minorId}`](/api-reference/applications/read-application-minor)** —
  read the minors (household members under 18) on an application: names, `birthDate`, `gender`,
  `relationship`, `studentType`, `alienRegistrationNumberLastFour` (last 4 characters only) and `status`
  (APPLICANT, RESIDENT or PRIOR). `minorId` is the same id the update and delete minor routes take. Needs the
  `applications: read` permission. Minors are not customers, so `/customers` is unchanged.
* **Deductions** — `GET /v1/deductions` and `GET /v1/deductions/{deductionId}` *(Defined, not yet published)*: affordable-housing deductions recorded per household member and certification. `type` is `CHILDCARE_FOR_WORK`, `CHILDCARE_FOR_SCHOOL`, `MEDICAL` or `DISABILITY`; `annualAmount` is in USD. Filter with `filter` on `customerId`, `applicantId`, `householdId`, `applicationId` or `certificationId`. `customerId` is set for adult members only; minors have no customer record, so use `applicantId` (the member's applicant record on the application, not the `applicantId` of applications). A member can have deductions on several certifications. Each record is one expense entry: a member can have several deductions of the same type on one certification, so add their `annualAmount` values for the total of that type. Deductions on deleted certifications or deleted households are not returned. Fortress replaces a certification's deduction records each time they are saved, so a deduction `id` changes on every edit and no field identifies one entry across edits: re-read a certification's deductions by `certificationId` instead of storing ids. The provider name entered in the resident portal is not returned. No existing route or field changes.
* **Incomes** — [`GET /v1/incomes`](/api-reference/incomes/list-incomes) and [`GET /v1/incomes/{incomeId}`](/api-reference/incomes/read-income) *(Defined, not yet published)*: one row per income line an applicant reported on the affordable income checklist (`type`, `payComponent`, `annualAmount`, `payRate`, `payFrequency`, `hoursPerWeek`, `source`, `lineNumber`), with the employer details of the applicant's current job (the job on file as of the last completed certification) on an unambiguous base-pay `JOB` line and of the previous job on `RECENTLY_ENDED_JOB` lines (`employerName`, `employerAddress`, `employerPhone`, `supervisorName`, `jobTitle`, `startDate`, `endDate`). Filter by `customerId`, `applicantId`, `applicationId` or `certificationId`, or read them under `/v1/customers/{customerId}/incomes` and `/v1/applications/{applicationId}/incomes`. `militaryBranch` and `payer` are declared but not populated yet. Minors' income is not included yet. Read-only; the key needs `incomes: read`.
* **Assets** — [`GET /v1/assets`](/api-reference/assets/list-assets) and [`GET /v1/assets/{assetId}`](/api-reference/assets/read-asset) *(Defined, not yet published)*, and `Customer.assets[]` (`GET /v1/customers/{customerId}?fields=assets.*`) will be populated once they are. The first entry of each list on an applicant's asset checklist (the first two for `other`), per certification, including checklists filled in on the older form; `listTruncated` marks a list that holds more. New `Asset` fields: `category`, `cashValue`, `disposedDate`, `disposedAmount`, `description`, `applicantId`, `applicationId`, `certificationId`, `householdId`, `propertyId`, `organizationId`, and `propertyAddress`, `mortgageBalance` and `rentReceived` (declared, always `null` for now). Existing `Asset` fields are unchanged. Institution name and account number are not returned yet. Needs the `assets: read` permission for `/v1/assets`.

#### Database Views

**Added**

* **ExportedDeductions** — new view over `ApplicantAffordableDeduction`.
* **ExportedIncomes** — new view over `AffordableQualificationChecklist.incomeChecklist` and `ApplicantJob`.
* **ExportedAssets** — new view over `AffordableQualificationChecklist.assetChecklist`.

### Property Special - October 22, 2026

#### API Reference

**New Fields**

* **[`GET /properties/{propertyId}/availableUnits`](/api-reference/units/list-available-units)** — `meta`
  gains `special`: the requested property's current move-in special as the property's leasing team
  maintains it in Fortress, with `description` (free text) and `startDate` (when it was listed, or `null`
  when no date was recorded). It is property-wide, not per unit, and is present even when no units are
  available. `null` when the property has no current special (none listed, cleared, or not yet started).

### API Updates - October 21, 2026

#### API Reference

**Changed**

* **Agents** — [`GET /v1/agents/{agentKey}/state`](/api-reference/agents/get-agent-state) adds `cadencePolicy` for `agentKey: cora`: the outreach cadence CORA executes for the property (`touchpoints[]`, optional `quietHours`, `expireAfterLastTouchpoint`). It is the property's stored policy, or the default (the baseline seed) when the property never set one or has no CORA configuration; not returned for `leslie`, whose response is unchanged. New read-only `cadencePolicy.isFallback`: `true` for that default, `false` for a stored policy. Staff set the policy in Fortress; there is no partner write. `expireAfterLastTouchpoint` replaces the fixed 7-day `CADENCE_EXPIRED` window: CORA cancels with `CADENCE_EXPIRED` once the applicant has gone that long without interaction after the last touchpoint (always returned; 7 days when the stored policy did not set it). When outreach stops, request consolidation, channel fallback and on/off are fixed CORA behaviour, not policy fields: CORA stops when the application is complete, canceled, denied or abandoned (a response or upload restarts the clock at offset 0), and runs the cadence whenever the agent is enabled (`isEnabled`). The examples show the seed and the Product faster cadence.

#### Webhooks

**Changed**

* **[`agent.state_changed`](/webhooks/webhook-events/webhooks/agent-state-changed)** — also sent when staff change the CORA cadence policy of a property. CORA re-reads the state route and applies the new `cadencePolicy` from its next touchpoint.

Fortress is still aligning the cadence policy with Fission Labs (CORA): its shape, the seed values, re-reading it on `agent.state_changed` (contract v0.20 names `agent.property_state_changed`), serving it on the state route instead of a dedicated `GET /properties/{propertyId}/agents/cora/cadencePolicy`, serving the default with `isFallback: true` instead of `null` or a back-filled seed, the added `isFallback` and `expireAfterLastTouchpoint`, and the contract v0.20 fields left out of the policy (`stopOn`, `deadlineCompression`, `consolidateRequests`, `invalidChannelAcceleration`, `isActive`).

### API Updates - October 20, 2026

#### API Reference

**Changed**

* **Compliance Documents** — [`GET /v1/compliance-documents/applications/{applicationId}/bundle`](/api-reference/compliance-documents/get-application-bundle) *(Defined, not yet published)*:
  * Requirement status precedence, first match wins, blockers first: `AWAITING_UPLOAD`, `WAITING_FOR_COMPLIANCE`, `WAITING_FOR_APPLICANT`, `UPLOADED`, `IN_REVIEW`, `VALIDATED`. `WAITING_FOR_COMPLIANCE` now ranks second whether CORA set it on a document or an open manual compliance review shows it, and `WAITING_FOR_APPLICANT` ranks ahead of `UPLOADED` and `IN_REVIEW`. For example, a requirement with one document `WAITING_FOR_COMPLIANCE` and others `NOT_YET_REVIEWED` reads `WAITING_FOR_COMPLIANCE` (was `UPLOADED`). `AWAITING_UPLOAD` still wins, and `REJECTED` documents still never count.
  * A `WAITING_FOR_COMPLIANCE` or `WAITING_FOR_APPLICANT` requirement can still hold `NOT_YET_REVIEWED` documents: look at the document statuses for work to pick up.
  * Fortress is still aligning the requirement status precedence with Fission Labs (CORA).

### Webhook & API Updates - October 19, 2026

#### Webhooks

**Changed**

* **[`compliance_review.closed`](/webhooks/webhook-events/webhooks/compliance-review-closed)** — new `decision` field: `APPROVED`, `DENIED` or `NONE`, what staff decided when they closed the review. `APPROVED` / `DENIED` set every covered document (the `documentIds`, or every document of the application at close time for an application-wide review) to `APPROVED` / `REJECTED` with `statusSetBy: fortress_user`, final for CORA, and clear its `reasonCodes` (`runId`, `extractedFields` and `reviewResult` stay as CORA's history); `NONE` changes no document. New `decidedDocumentIds`: the documents the decision set (`[]` for `NONE`). After `DENIED` a requirement goes back to `AWAITING_UPLOAD` when nothing else counts or a required document type is left without a counting document. `compliance_review.opened` is unchanged.

#### API Reference

**Changed**

* **Documents** — [`POST /v1/documents/{documentId}/results`](/api-reference/documents/post-document-results) *(Defined, not yet published)*:
  * Needs the `documents: create` permission.
  * `404` (code `DOCUMENT_NOT_FOUND`) also when the body's `applicationId` is not the document's application, and for a document that is not `COMPLETED`, has no applicant or no document type (same `404` as an unknown document).
  * `409` codes, first match wins: `DOCUMENT_REMOVED`, `COMPLIANCE_REVIEW_OPEN`, `STATUS_SET_BY_USER`, `DOCUMENT_ALREADY_VALIDATED`. The new `DOCUMENT_ALREADY_VALIDATED` means the document is already `VALIDATED_BY_CORA`: any further write is refused and a new upload is a new document. A re-run after `REJECTED` is allowed. `STATUS_SET_BY_USER` also applies after a review closed with `NONE` on a document staff decided earlier.
  * Merge rules: with the same `runId`, `extractedFields` merge by `canonicalName`, `checks` by `checkId` and `computed` by `name`, and properties not sent keep their stored values. A new `runId` replaces the run, and properties not sent are cleared. `reasonCodes` are always replaced.
  * No array size limits, only the 6 MB request size limit. No transition rules beyond the `409`s.
  * The requirement status is derived when the bundle is read.
  * Fortress is still aligning `DOCUMENT_ALREADY_VALIDATED` and merging `computed` by name with Fission Labs (CORA).

### API Updates - October 16, 2026

#### API Reference

**Changed**

* **Compliance Documents** — [`GET /v1/compliance-documents/applications/{applicationId}/bundle`](/api-reference/compliance-documents/get-application-bundle) *(Defined, not yet published)*:
  * **Default response shape change:** `label`, `taxonomyDomain`, `recognition`, `fields`, `mappings` and `rules` are no longer returned unless the new `include` parameter asks for them (`include=requirements.documentTypes.label,requirements.documentTypes.taxonomyDomain,requirements.documentTypes.recognition,requirements.documentTypes.fields,requirements.documentTypes.mappings,requirements.documentTypes.rules`). Each document type still carries its `documentTypeId`, `documentTypeKey`, `required` and `definitionVersion`; read the descriptors from the [document type definitions](/api-reference/compliance-documents/get-compliance-documents-manifest) route by `documentTypeKey`, and the definition content by `documentTypeId` + `definitionVersion`. `exclude` wins over `include`. A bundle with definitions can exceed the response size limit for large households: above 6 MB the gateway answers `502`; retry without `include`.
  * New `required` property on each document type: `true` when Fortress asks the applicant for it and it is not optional, `false` for an optional document type or the type of another uploaded document. Each requirement's `documentTypes` lists the documents Fortress asks the applicant for (the same cards as in MyFortress, optional ones included), then the type of any other document uploaded to it.
  * Needs the `compliance-documents: read` permission (`403` when the key is missing, unknown or lacks it; `401` only when the backend refuses a key that passed the gateway). `404 APPLICATION_NOT_FOUND` (`{ code, message }`) for a missing application or one outside the key's organization or properties; the same response for both. `502` when Fortress cannot answer right now or the response is above 6 MB.
  * `requirementType` is the requirement name: `income`, `assets`, `deductions.and.allowances` or `identity.and.composition`, the name of the `requirementId` the `requirement.upload_*` events carry. Fortress is still aligning the requirementType values with Fission Labs (CORA).
  * Definition versions: the bundle serves the current definition version for each document type instead of pinning it at signing. Compare `definitionVersion` to pick the Blueprint; a `definitionVersion` with no Blueprint: re-read the manifest. Any change to an approved definition is a new `definitionVersion`; a definition that is not approved can change without a new version and is served as `definitionVersion: null`.
  * Requirement status: `AWAITING_UPLOAD` when no non-rejected document counts, or a required document type (`required: true`) has none; a `REJECTED` document sends the requirement back to `AWAITING_UPLOAD` when nothing else counts or it was the only counting document of a required type. The status follows the applicant's current answers and can change between events; re-read when in doubt.
  * New `complianceReview` property: the application's open manual compliance review (`sessionId`, `documentIds`, `openedAt`, the same values as `compliance_review.opened`), or `null` when none is open. A closed review is gone from the bundle; `compliance_review.closed` reports the close. Always returned; use it to recover a missed review event.
  * While a manual compliance review is open, the documents it covers (all of them for an application-wide review) show `WAITING_FOR_COMPLIANCE` with `statusSetBy: fortress_user`, an earlier human `APPROVED` or `REJECTED` included, and their requirements show `WAITING_FOR_COMPLIANCE` (a new rule, right after `AWAITING_UPLOAD`, which still wins when a required document type has no document). The override is derived on read: closing with `NONE` shows the stored statuses again; `APPROVED` or `DENIED` stores the staff decision.
  * The SDK samples show each scenario's own query (`exclude`, `include`, `hideDocumentTypesOnValidatedRequirement`), and the examples use the government ID (`identity_government_issued_id`) for each adult's identity requirement.
* **Compliance Documents** — [`GET /v1/compliance-documents/manifest`](/api-reference/compliance-documents/get-compliance-documents-manifest) adds three income document types, `public_assistance_benefits_letter`, `rental_assistance_voucher` and `other_income`, with `definitionStatus: in_review`. This is a data change in Fortress' manifest, not a contract change. *Defined, not yet published.*

### API Updates - October 14, 2026

#### API Reference

**New Endpoints**

* **Compliance Documents** — [`GET /v1/compliance-documents/manifest`](/api-reference/compliance-documents/get-compliance-documents-manifest) returns every document type with its current definition (fields, mappings, rules), `definitionVersion` and `definitionStatus`, independent of any application or upload, for building document blueprints before any upload. One version per document type; definition changes are communicated manually (no event). Needs the `compliance-documents: read` permission (`403` when the key is missing, unknown or lacks it). *Defined, not yet published.*

### API Updates - October 13, 2026

#### API Reference

**Changed**

* **Prospects** — [`POST /prospects/{prospectId}/portal-invitation`](/api-reference/prospects/create-prospect-portal-invitation)
  now requires Leslie to be enabled for the prospect's property (`400` otherwise, before any portal
  account is read or written). It returns the portal login link with `isClaimed: true` when the
  prospect's email already has a claimed account at the property on another record, and a portal
  account at another property no longer blocks the invitation. It returns `400` instead of a claim
  link when the prospect has no usable email address (missing or a placeholder), when another record
  holds a pending invitation for that email at the property, or when the property blocked the
  prospect's portal account. A claim link is only ever returned for an account under the prospect's
  own email address.

### Webhook & API Updates - October 12, 2026

#### Webhooks

**Changed** — now *Available*:

* **[`agent.state_changed`](/webhooks/webhook-events/webhooks/agent-state-changed)** — delivered when staff enable or disable an agent (`cora` or `leslie`) for a property in Fortress' agent configuration. Sent for every agent, so ignore events whose `agentKey` is not yours. `reason` is always a string: the reason staff gave, `(configuration deleted)` when deleting an enabled configuration disabled the agent, or `(not supplied: legacy leslie-configs route)` when it is unknown. Sent with `scope: PROPERTY` to that property's active subscriptions that list the event, with `organizationId` and `propertyId` set and `householdId` `null` (`organizationId` is the owning organization, not a scope). `GLOBAL` and `HOUSEHOLD` may also be sent; handle every scope from the payload.
* **[`compliance_review.opened`](/webhooks/webhook-events/webhooks/compliance-review-opened)** — delivered once per session when staff open a manual compliance review on an application, to each active property subscription that lists the event. The review blocks the whole application and covers the `documentIds` (every document when the array is empty). While it is open, the cancel route answers `409 COMPLIANCE_REVIEW_OPEN`.
* **[`compliance_review.closed`](/webhooks/webhook-events/webhooks/compliance-review-closed)** — delivered once when staff close the review, with the same `sessionId` and `documentIds` and `closedAt` set. Closing an already closed session sends nothing.

#### API Reference

**New Endpoints**

* **Agents** — [`GET /v1/agents/{agentKey}/state?propertyId={propertyId}`](/api-reference/agents/get-agent-state) returns whether an agent (`cora` or `leslie`) is enabled for a property (`propertyId`, `agentKey`, `isEnabled`, `updatedAt`). A property never configured, or whose agent configuration was deleted, returns `isEnabled: false`. Needs the `agents: read` permission (`403` when the key is missing, unknown or lacks it); `400 INVALID_PROPERTY_ID`, `404 AGENT_NOT_FOUND`, `404 PROPERTY_NOT_FOUND` (missing or not the key's property). Read it on startup and after a missed webhook.

### Webhook Updates - October 9, 2026

#### Webhooks

**Changed** — now *Available*:

* **[`requirement.upload_received`](/webhooks/webhook-events/webhooks/requirement-upload-received)** — delivered when a document is uploaded to a requirement (`income`, `assets`, `deductions.and.allowances` or `identity.and.composition`) through the MyFortress portal task list or the staff Documents tab.
* **[`requirement.upload_removed`](/webhooks/webhook-events/webhooks/requirement-upload-removed)** — delivered when a Fortress user deletes a document from a requirement. The portal's Restart Application does not fire it.

### API Updates - October 9, 2026

#### API Reference

**Changed**

* **Applications** — [`POST /v1/applications/{applicationId}/cancel`](/api-reference/applications/cancel-application) now documents the `applications: cancel` permission requirement and the `403` response when the API key lacks this permission.

### API Updates - October 8, 2026

#### API Reference

**Added**:

* **Work Orders** — [`GET /v1/workOrders/{workOrderId}/attachments/{attachmentId}/downloadUrl`](/api-reference/work-orders/read-work-order-attachment-download-url) returns a short-lived signed link to a work order attachment (PDF, JPEG, PNG, or GIF), and the link expires after 300 seconds by default (`expiresInSeconds` is clamped to 60–900). Marked *Available by Request*: the API key needs an explicit `workOrders` read grant for this route, even when it already works on the other work-order routes. Contact our team to request it.

### Reaction Flag - October 7, 2026

#### Webhooks

**Changed**

* **[`message.received`](/webhooks/webhook-events/webhooks/message-received)** — `data` gains
  `isReaction`: `true` when the message is a phone tapback reaction (for example `Loved "See you at 3"`),
  which phones deliver as ordinary text. The event still fires for reactions; responders should
  typically not reply when it is `true`.

#### API Reference

**New Fields**

* **`GET /messages`** and **`GET /messages/{messageId}`** — `isReaction`: the same flag as on
  `message.received`. Always `false` for outbound messages.

### Webhook & API Updates - October 5, 2026

#### API Reference

**Added** — *Defined, not yet published*:

* **Webhooks** (new section) — [`POST /v1/webhooks/trigger`](/api-reference/webhooks/trigger-webhook-event) sends an example delivery of a CORA event (`{ "eventType": "<event>", "organizationId": "<uuid>", "propertyId": "<uuid>" }`, all required) only to the active `cora` subscriptions of that property that list that event type (otherwise `404` `NO_SUBSCRIPTION`), signed and enveloped like a real delivery, so CORA can test its receiver before the real flow sends that event. The payloads are examples, not real data. See [Testing your receiver](/ai-team-members/cora/events#testing-your-receiver).

### Webhook & API Updates - October 1, 2026

#### AI Team Members

**Changed**

* **CORA integration pages** — Overview, Review Flow, Events and API now describe the bundle model: each requirement lists its document types and their documents, CORA writes document status, and Fortress derives requirement status.

#### API Reference

**Added** — *Defined, not yet published*; specified in the CORA integration OpenAPI:

* **Compliance Documents** (new section) — [`GET /v1/compliance-documents/applications/{applicationId}/bundle`](/api-reference/compliance-documents/get-application-bundle)
* **Documents** — [`POST /v1/documents/{documentId}/results`](/api-reference/documents/post-document-results)
* **Applications** — [`POST /v1/applications/{applicationId}/cancel`](/api-reference/applications/cancel-application)

**Removed** — endpoints superseded by the compliance-documents bundle or, for the two `coraConfiguration` routes, by the `agent.state_changed` event (CORA's enablement). The two `coraConfiguration` routes were served and are now removed from these docs. The rest were *Available by Request* and never served, so no live integration is affected:

* **Agent Review** (section removed) — `GET /agentReviewReasonCodes`, `GET /agentReviewReasonCodes/{code}`, `GET /documentReviewCriteria`, `GET /documentReviewCriteria/{criteriaId}`
* **Applications** — `GET /applications/{applicationId}/requirements`, `POST /applications/{applicationId}/requirements/{requirementId}/findings`, `POST /applications/{applicationId}/corrections`, `POST /applications/{applicationId}/resignatureRequests`
* **Households** — `GET /households/{householdId}/complianceReview`, `GET /households/{householdId}/coraConfiguration`
* **Properties** — `GET /properties/{propertyId}/agents/cora/cadencePolicy`, `POST` / `GET /properties/{propertyId}/agents/cora/alerts`, `GET /properties/{propertyId}/coraConfiguration`

#### Webhooks

**Added** — *Defined, not yet published*; specified in the CORA integration OpenAPI:

* **[`agent.state_changed`](/webhooks/webhook-events/webhooks/agent-state-changed)** — staff enable or disable the document-review agent. Each `scope` (`GLOBAL`, `ORGANIZATION`, `PROPERTY`, `HOUSEHOLD`) carries only its own id; the others are `null`. Only the global kill switch (`scope: GLOBAL`, no ids) is sent at first, once per property subscription.

**Changed** — payloads replaced with the CORA integration OpenAPI definitions:

* **[`requirement.upload_received`](/webhooks/webhook-events/webhooks/requirement-upload-received)**, **[`requirement.upload_removed`](/webhooks/webhook-events/webhooks/requirement-upload-removed)** — one shared payload. `applicantId` and `documentId` are always set, and `requirementVersion` is removed. Still *Defined, not yet published*.
* **[`compliance_review.opened`](/webhooks/webhook-events/webhooks/compliance-review-opened)**, **[`compliance_review.closed`](/webhooks/webhook-events/webhooks/compliance-review-closed)** — one shared payload with a single `applicationId` instead of `applicationIds`. Still *Defined, not yet published*.
* **[`application.completed`](/webhooks/webhook-events/webhooks/application-completed)** — `status` lists only the five final statuses (`APPROVED`, `CANCELED`, `DENIED`, `ABANDONED`, `WAITLIST`); `applicationId` tells CORA which application it is done with. Also sent with `status: CANCELED` after `POST /v1/applications/{applicationId}/cancel`. Still *Available*.

**Removed** — eleven events that were *Defined, not yet published* or *Draft*. None was ever *Available*, so no live subscription is affected. The CORA integration does not use them:

* `application.activated`, `application.approved`
* `agent.property_state_changed`, `agent.global_state_changed`
* `requirement.satisfied`, `requirement.replacement_requested`, `requirement.needs_compliance_review`, `requirement.added`, `requirement.changed`, `requirement.canceled`, `requirement.reviewer_finding_submitted`

**Removed** — three more events. None was ever delivered:

* `signature.completed` — covered by [`application.signed`](/webhooks/webhook-events/webhooks/application-signed)
* `application.corrected` — not used by CORA
* `application.updated` — not used by CORA and not in the integration OpenAPI

## September 2026

### Webhook Updates - September 28, 2026

#### Webhooks

**Changed**

* **[`message.received`](/webhooks/webhook-events/webhooks/message-received)** — `data` gains
  `personStatus`, nullable: the recipient's current status (`APPLICANT`, `PROSPECT`, `RESIDENT`, or
  `PRIOR_RESIDENT`). A recipient with a customer record is classified by that record's status, even
  if they were converted from a prospect; only a recipient who is still just a prospect is
  `PROSPECT`. `null` when Fortress cannot classify the person.

**Now Available**

* **Person-status subscription filtering** — a subscription can now request only the person statuses
  it wants to receive. See [Subscribing](/webhooks/webhooks#subscribing). A filtered subscription receives
  a `message.received` event only when its `personStatus` is in the list; event types without
  `personStatus` are unaffected. Omit the filter to receive all statuses.

### Prospect Portal Invitations - September 26, 2026

#### API Reference

**New Endpoints** — marked *Available by Request*

* **[`POST /prospects/{prospectId}/portal-invitation`](/api-reference/prospects/create-prospect-portal-invitation)** —
  returns the prospect's resident portal link so you can invite them to register and apply, creating
  their portal account if needed. Returns the portal login link once the account is claimed.

**New Fields**

* **[`GET /prospects`](/api-reference/prospects/list-prospects)** and
  **[`GET /prospects/{prospectId}`](/api-reference/prospects/read-prospect)** — `portalInviteUrl`: the
  prospect's claim link while their portal account is unclaimed, or the portal login link once claimed.
  Omitted until a portal account exists or when the property's resident portal is not active.

### Webhook Updates - September 26, 2026

#### Webhooks

**Now Available**

* **[`application.completed`](/webhooks/webhook-events/webhooks/application-completed)** — delivered today.
  It fires when an application's status moves to `APPROVED`, `CANCELED`, `DENIED`, `ABANDONED` or
  `WAITLIST`, no longer when the signature step completes. `status` carries the outcome that completed it,
  and `data` gains `customerId` (the household's main customer) and `prospectId`, both nullable. Add
  `application.completed` to a subscription's event types to receive it.

**Changed**

* **[Webhook Events](/webhooks/webhook-events)** — the events index is split into two tables: events that are
  *Available* today, and events that are *Defined, not yet published*.

**Deprecated**

* **`application.approved`** — superseded by
  `application.completed` and will not be published. (Page removed October 1, 2026.)

### Webhook Updates - September 25, 2026

#### Webhooks

**New**

* **[`application.signed`](/webhooks/webhook-events/webhooks/application-signed)** — delivered each time
  the last required adult in a household e-signs their application form. `occurredAt` is the signed time.
  Dedupe on `eventId`, which is the same on every delivery of one signing. `lastSignerApplicantId` names
  the household member whose signature completed it.

**Changed**

* **[`message.received`](/webhooks/webhook-events/webhooks/message-received)** — `householdId` is now
  set when the recipient is a customer: the household that customer belongs to. It was `null` on every event
  before this change. Prospect recipients still carry `null`.

**Corrected**

* **[`message.received`](/webhooks/webhook-events/webhooks/message-received)** — the event page was
  wrong in three places, and now documents the fan-out with a worked example:
  * It said the event fires **once per inbound SMS**. It fires **once per recipient** — every customer
    or prospect whose number matches the texter's — so one SMS to a number shared by several people
    raises several events carrying the same `messageSid`.
  * It said at most one of `customerId` and `prospectId` is set. A customer converted from a prospect
    carries both.
  * It said an unrecognized number fires the event with every recipient id `null`. It raises **no**
    event, and the message is not stored.
* **[Webhooks › Idempotency](/webhooks/webhooks#idempotency)** — added `messageSid` to the identifiers
  you must **not** deduplicate on. The events one SMS fans out to share it but describe **different
  people**; collapsing on it silently discards real recipients. `eventId` is the only deduplication key.
  This guidance was already correct here and only contradicted on the `message.received` page.

### Documentation & API Updates - September 21, 2026

#### AI Team Members

**New Documentation**

* **[CORA](/ai-team-members/cora/overview)** — a new section for the document-review agent integration: [Overview](/ai-team-members/cora/overview) (requirement slots, the one agent write, guardrails), [Review Flow](/ai-team-members/cora/review-flow), an [Events](/ai-team-members/cora/events) index and an [API Reference](/ai-team-members/cora/api) index. Both indexes link into the shared reference pages below.

#### API Reference

**New Endpoints** — all marked *Available by Request*

* **Properties** — `GET /properties/{propertyId}/coraConfiguration`, `GET /properties/{propertyId}/agents/cora/cadencePolicy`, `POST` / `GET /properties/{propertyId}/agents/cora/alerts`.
* **Households** — `GET /households/{householdId}/coraConfiguration`, `GET /households/{householdId}/complianceReview`.
* **Applications** — `GET /applications/{applicationId}/requirements`, `POST …/requirements/{requirementId}/findings`, `POST /applications/{applicationId}/corrections`, `POST /applications/{applicationId}/resignatureRequests`.
* **Documents** (new section) — [`GET /documents/{documentId}/downloadUrl`](/api-reference/documents/read-document-download-url).
* **Agent Review** (new section) — `GET /agentReviewReasonCodes`, `GET /agentReviewReasonCodes/{code}`, `GET /documentReviewCriteria`, `GET /documentReviewCriteria/{criteriaId}`.

#### Webhooks

**New Events** — 19 events, status *Defined, not yet published* or *Draft*. Fourteen of them were removed again on October 1, 2026 (see above); the rest have their own page under [Webhook Events](/webhooks/webhook-events):

* `application.activated`, `application.approved`, `application.completed`, `application.corrected`, `application.updated`, `signature.completed`
* `requirement.upload_received`, `requirement.upload_removed`, `requirement.satisfied`, `requirement.replacement_requested`, `requirement.needs_compliance_review`, `requirement.added`, `requirement.changed`, `requirement.canceled`, `requirement.reviewer_finding_submitted`
* `compliance_review.opened`, `compliance_review.closed`, `agent.property_state_changed`, `agent.global_state_changed`

**Changed**

* **[Webhook Events](/webhooks/webhook-events)** — the **Status** column now has a legend: *Available*, *Defined, not yet published*, *Draft*.
* **[`prospect.created`](/webhooks/webhook-events/webhooks/prospect-created)**, **[`message.received`](/webhooks/webhook-events/webhooks/message-received)** — links to the Webhooks guide were relative and resolved to a missing page; they are absolute now.

#### Leslie Integration Settings

**Removed**

* **`PUT /properties/{propertyId}/leslieConfiguration`** — the property Leslie configuration is now
  read-only on this API. Enabling or disabling an agent, and changing its settings, is managed by
  Fortress staff; a change to `isEnabled` was announced as the
  `agent.property_state_changed` webhook, since removed (October 1, 2026). The matching `GET` is unchanged.
* **`webhookUrl`** — removed from the property Leslie configuration read shape. Deliveries are
  configured per subscription rather than per configuration record.

---

### Schema & Database View Updates - September 11, 2026

#### Prospects API

**New Column on `exported_prospects`**

* **`isPrimaryProspect`** (boolean, nullable) — Whether the prospect is the primary prospect: the
  household member Fortress marks as **Primary** on the application the prospect converted into. Read
  through the prospect's applicant record, so `true` is the head of household, `false` is a non-primary
  member of that household, and `null` means the prospect has not been converted to an applicant yet and
  the status cannot be determined.

**Schema Changes**

* **`GET /prospects`** and **`GET /prospects/{prospectId}`** — responses now include `isPrimaryProspect`.
  The field is filterable (`isPrimaryProspect = true`, `= false`, or `= NULL` for the undetermined
  records). See [Primary Prospect](/api-reference/prospects#primary-prospect) for the full semantics.

**Notes for Integrators**

* The field is additive — no existing field changed name, type, or meaning, and the list and read
  endpoints return the same value for the same prospect.
* `null` is "unknown", not "not primary". An unconverted prospect is the only person on its own record,
  but the marker lives on the application household, so it stays undetermined until a conversion creates
  one. Undetermined records come back **without** the key (null columns are omitted, as for every
  nullable field on this API) — treat a missing `isPrimaryProspect` as `null`; `= NULL` filters match them.
* Clients with a generated type for the prospect resource should add the field as an optional,
  nullable boolean.

---

### Documentation & API Updates - September 1, 2026

#### Webhooks

**New Documentation**

* **[`message.received`](webhooks/message-received)** — Per-event payload reference for the newly
  published inbound-message event.

**New Event**

* **`message.received`** — Delivered when an inbound message is received for a property your
  subscription covers. The `data` payload carries `messageId`, `conversationId`, `propertyId`,
  `organizationId`, `customerId`, `prospectId`, `householdId`, `from`, `to`, `content`, `messageSid`, and
  `receivedAt`. Field names match the Floo message and conversation resources where they overlap;
  `messageId` is the message resource's `id`.

**Changed**

* **[Webhook Events](webhook-events)** — `message.received` moves from "Defined, not yet published" to
  **Available**. The older two-way-messaging notification path is retired as of this change; this webhook
  is now the only notification Fortress sends for an inbound message.

**Notes for Integrators**

* Delivery is at-least-once, the same as every other event — dedupe on `eventId`, never on `messageId` or
  on `X-Fortress-Delivery-Attempt`. See [Idempotency](/webhooks/webhooks#idempotency).
* ~~`customerId` and `prospectId` are both `null` when the sender is not a recognized contact; at most one
  of the two is ever set.~~ *Corrected September 25, 2026:* a number Fortress does not recognize raises
  no event, and a customer converted from a prospect carries both ids. See the September 25 entry above.
* `message.received` does not fire for an opt-out (`STOP`) or opt-in keyword reply, or when the receiving
  number does not match a property Fortress has on file. See
  [When it does not fire](webhooks/message-received#when-it-does-not-fire).

---

## August 2026

### Documentation & API Updates - August 28, 2026

#### Webhooks

**Documentation**

* **[Webhook Events](webhook-events)** — each event type now has its own page, starting with
  **[`prospect.created`](webhooks/prospect-created)**. The combined event reference has been split so a
  new event adds a page rather than growing one.
* **[Webhooks](webhooks)** — new *How a delivery works* section, with a sequence diagram of a delivery and
  a worked example of the exact request your endpoint receives, headers included.
* **[Webhooks](webhooks)** — `X-Fortress-Delivery-Attempt` is documented as informational only. A value
  greater than `1` usually means an earlier attempt failed, but a redelivery of an already-delivered
  event also carries one, and the value can repeat across duplicates. Dedupe on `eventId`, never on this
  header.

---

### Documentation & API Updates - August 26, 2026

#### Leslie Integration Settings

**Changed**

* **`propertyLeslieConfigurations` — secret values are now redacted on read.** The `settings` object can
  carry a validation key; that key, and any other secret value inside `settings`, is write-only from now
  on — a `GET` never returns it.

---

### Documentation & API Updates - August 25, 2026

#### Webhooks

**New Documentation**

* **[Webhooks](webhooks)** — Fortress now pushes signed event notifications to partner endpoints instead
  of requiring you to poll for changes. The new guide covers the delivery envelope, the
  `X-Fortress-*` headers, step-by-step signature verification with Node.js and Python examples, secret
  rotation, the retry and timeout rules, and idempotency requirements.
* **[Webhook Event Reference](webhook-events)** — Per-event payload reference, starting with
  `prospect.created`.

**New Event**

* **`prospect.created`** — Delivered when a new prospect is created for one of your properties. The `data`
  payload carries `prospectId`, `propertyId`, `organizationId`, `phone`, `firstName`, `referralSource`,
  `isManual`, and `createdAt`. Field names match the Floo prospect resource, so the same names mean the
  same things on `GET /prospects/{prospectId}` — `prospectId` is the resource's `id`.

**Notes for Integrators**

* `isManual` is `true` only when a person hand-entered the prospect in the Fortress UI, and `false` for a
  prospect created through the API, through an application conversion, or by an automated process. Bulk
  imports do not raise `prospect.created` at all, so `isManual: false` never tells you "this was an
  import" — that case is not observable from this event.
* Subscriptions are provisioned by Fortress. There is no self-serve endpoint to create, list, or delete a
  webhook subscription — contact [integrations@fortresstech.io](mailto:integrations@fortresstech.io) with your HTTPS URL and the events you want.
* A subscription is scoped to a single **property**, not to your organization. If you cover multiple
  properties, you have one subscription per property, each with its own signing secret — and each can
  point at a different URL if you want events for different properties delivered to different endpoints.
  This does not change the event payload: `data.organizationId` is still included on every event so you
  know which organization it belongs to.
* Signatures are `HMAC-SHA256` over exactly `<t>.<rawBody>`, hex-encoded, sent as
  `X-Fortress-Signature: t=<unix seconds>,v1=<hex>`. Verify against the **raw request body bytes**;
  re-serializing the parsed JSON changes the bytes and the digest will never match. Tolerance is 300
  seconds.
* During a secret rotation a second `,v1=<hex>` is appended to the same header. Accept the delivery if
  **any** `v1` matches the secret you hold, and loop over every `v1` — reading only the first breaks
  intermittently during rotation.
* Delivery is at-least-once, so you **must** dedupe on `eventId`, and you must respond `2xx` within 10
  seconds and do your processing asynchronously. `5xx`, `408`, `429`, and transport errors are retried
  after the delivery queue's visibility timeout; any other `4xx` is treated as a permanent rejection,
  recorded, and is **not** retried. Redirects are not followed. The retry schedule and dead-letter
  behavior for a delivery that keeps failing are not finalized yet.
* Events are not backfilled and bulk imports are suppressed, so webhooks are an ongoing-change feed, not a
  way to load initial state. A prospect your own integration created through the API does not generate a
  delivery back to you.
* This is additive documentation. No existing endpoint, field, or schema changed.

---

### Documentation & API Updates - August 19, 2026

#### Work Orders API

**Schema Changes**

* **`POST /workOrders`** and **`PUT /workOrders/{workOrderId}`** — `requestorEmail` and `requestorPhone`
  are now explicitly nullable and accept a blank value. Sending an empty or whitespace-only string is
  treated as no value rather than rejected, which matches what the Fortress UI has always allowed when a
  work order is logged without requestor contact details. A non-blank value is still validated:
  `requestorEmail` must be a valid email address of at most 100 characters, and `requestorPhone` is still
  limited to 15 characters.
* **Work order responses** — `requestorEmail` and `requestorPhone` are now documented as nullable. This
  corrects the published contract rather than changing behavior: both fields could already come back
  `null`, and a blank value submitted on create or update is now stored and returned as `null`.

**Notes for Integrators**

* Blank and `null` are equivalent on input. Both mean "no requestor contact detail" and both persist as
  `null`, so a form that submits untouched inputs as empty strings no longer needs to strip them.
* On update, omitting a field and sending `null` are **not** the same. Omitting `requestorEmail` leaves any
  stored value untouched; sending an explicit `null` — or a blank string — clears it. Send the field only
  when you intend to change it.
* No field changed name or type, and neither field was ever required, so no existing integration needs to
  change. Clients that pinned a non-nullable type for these two response fields should widen it to accept
  `null`.

---

### Documentation & API Updates - August 13, 2026

#### Screening API

**Schema Changes**

* **`GET /screening-requests/{screeningRequestId}`** — `screening.payload` gains `unit`: the unit the
  screening is for, with `id`, `number`, and a nested `floorplan` object carrying the floor plan's `id`,
  `internalName` and `marketingName`. Occupancy wins over the applied-for unit: whenever the household
  already occupies a unit, that is the unit reported, even when the application is for a different one.
  Only when the household occupies nothing does this fall back to the unit most recently assigned to the
  application, which is the usual case for a new applicant. A household part-way through a transfer
  reports the unit it still occupies, not the transfer destination, until the transfer completes.

**Notes for Integrators**

* Both floor plan names are returned as separate fields. `internalName` is the name staff use inside
  Fortress — the floor plan endpoints expose this same value as `name` — and `marketingName` is the
  public-facing name. Either may be `null` independently.
* Assigned units are not required in order to screen, so `unit` is `null` when the household has no unit
  assigned, and `unit.floorplan` is `null` when the unit has no floor plan. Neither case is an error.
* `unit` is also `null` when the unit could not be resolved as the screening was submitted. That lookup is
  deliberately non-blocking so it can never fail a screening, which means a transient failure is
  indistinguishable from an unassigned unit.
* The field is additive — no existing field changed name, type, or meaning.
* `unit` is populated once when the screening is submitted and never recalculated, so it is absent entirely
  on screening requests created before this release, and a `null` is never backfilled afterwards. Treat a
  missing `unit` the same as `null`.

---

### Documentation & API Updates - August 12, 2026

#### Messaging API

**New Endpoint**

* **`PATCH /messages/{messageId}/needsHelp`** — Flag or resolve a message's needs-help state. `isFlagged: true`
  raises the flag, `isFlagged: false` resolves it; the response echoes the message plus a rollup of the
  conversation's needs-help state.

**Schema Changes**

* **`GET /messages/{messageId}`** now exposes eight `needsHelp*` fields: `needsHelp`, `needsHelpReason`,
  `needsHelpFlaggedByType`, `needsHelpFlaggedById`, `needsHelpFlaggedAt`, `needsHelpResolvedByType`,
  `needsHelpResolvedById`, `needsHelpResolvedAt`. All are absent on messages that have never been flagged.
* **`GET /conversations`** adds `needsHelpMessageIds`, the ids of every currently-flagged message on the
  conversation, and `conversationStatus` gains a new value, `NEEDS_HELP`. The `status` query parameter on
  `GET /conversations` accepts it too.

**Notes for Integrators**

* `NEEDS_HELP` is a different signal from `UNRESOLVED`: it is per-message and typically agent-raised, versus
  `UNRESOLVED`'s thread-level staff triage. `NEEDS_HELP` prevails over the underlying workflow status for as
  long as any flag is open, and the conversation falls through to whatever status that workflow currently
  calls for once the last flag resolves.
* The request is idempotent — flagging an already-flagged message, or resolving one that isn't flagged,
  returns `200` with the current state rather than an error.
* Flagging or resolving requires the `update` action on the `messages` permission scope. That grant is
  service-wide: it authorizes every write under `/v1/messages/*`, not only this endpoint.

> **Note**
>
> This endpoint is part of our evolving API ecosystem. To explore availability for your integration, please
> contact our team.

---

### Documentation & API Updates - August 11, 2026

#### Properties API

**New Property Configuration Endpoints**

* **`GET /properties/{propertyId}/configuration`** — The extended configuration record for a property: marketing copy, contact routing, resident-facing links, leasing and application settings, master metered utilities, service vendors and site details. Roughly 60 fields that were previously unavailable through the API.
* **`GET /propertyConfigurations/{propertyConfigurationId}`** — The same record as a bare object rather than an array. A property's configuration id is the same as its property id.

**What the record covers**

* **Marketing** — `propertyDescription`, `shortDescription`, `officeHoursDescription`, `officeHours`, `petPolicyDescription`, `parkingDetailDescription`, `amenities`, `marketingUrl`.
* **Contact** — `email`, `phone`, `faxNumber`, and `textingPhoneNumber` for properties with two-way communication enabled.
* **Address** — the physical address, with `physicalState` resolved to a two-letter code.
* **Links** — `availabilityUrl` and `applicationPortalUrl`.
* **Leasing and applications** — `lateFeeMethod`, `acceptingOnlineApplications`, `applicationIncomeRequirement`, `allowedLeaseTermMonths`, `quoteExpiration`.
* **Utilities** — `masterMeteredElectricity`, `masterMeteredGas`, `masterMeteredSewer`, `masterMeteredTrash`, `masterMeteredWater`, `masterMeteredOther`.
* **Security deposit interest** — `accrueSecurityDepositInterest`, `securityDepositInterestPayoutFrequency`, `securityDepositSimpleInterestRate`.
* **Geography and time** — `nearestMetroArea`, `timezone`, `goesOnDaylightSavings`, `countyName`.
* **Lifecycle dates** — `siteBuiltDate`, `lastRenovationDate`, `managementTookOverDate`, `acquisitionDate`, `managementQuitDate`.
* **Site details** — `officeAlarmCode`, `hasGateWithCode`, `hasFitnessCenter`, `hasPool`, `hasPackageDelivery`, `nonSmoking`, `petWeightMax`, `leadPaintDisclosure`.
* **Contacts and vendors** — `clientRepresentativeName`, `accountantName`, `apClerk`, and the water, gas, electric, cable, internet, pest and trash companies.
* **Fees and periods** — `feesStartDate`, `currentPeriodStartDate`, `currentPeriod`, `buildingProtectionFee`, `leaseRentPercentage`, `chargesFacilityFeeForLiabilityInsurance`, `facilitiesFeeAmount`, `turnMoveOutDays`, `evictionStartDate`, `leaseNoticeToVacateDays`, `emergencyEntranceNotice`.

**Notes for Integrators**

* `amenities`, `officeHours` and `allowedLeaseTermMonths` flatten a one-to-many relationship into a delimited string and are **unordered sets** — sort client-side if order matters. Every `officeHours` entry carries its day name for that purpose.
* `lateFeeMethod` and `securityDepositSimpleInterestRate` are kept as a dated history on the property, so both report the value in effect today.
* `quoteExpiration` is a duration (`SAME_DAY`, `HOURS_24`, `HOURS_48`, `HOURS_72`), not a calendar date.
* `siteBuiltDate` and `lastRenovationDate` are free text rather than strict dates, so they may hold a year or a partial date.
* `applicationPortalUrl` is only populated while the resident portal is active for the property; `availabilityUrl` is always present.
* `timezone` is an IANA name, or null for the small number of properties with no timezone configured.
* The subresource follows the usual convention and returns an array holding the single record. Use `/propertyConfigurations/{id}` for an object.
* Boolean fields are serialised as the strings `"true"` and `"false"` rather than JSON booleans. This applies to every boolean across the API, not just this resource.

#### Properties Export

**New Column on `exported_properties`**

* **`propertyClass`** (varchar) — The property's class, resolved to its readable name: `Conventional`, `Affordable` or `Mixed`. Null for properties with no class assigned.

**Impact on API:** The `/properties` endpoint now includes `propertyClass` in API responses and data exports, and it can be used in `filter` and `sort`.

---

### Documentation & API Updates - August 2, 2026

#### Messaging API

**New Messaging Endpoints**

* Added the [Messaging](/api-reference/messaging/list-conversations) section covering two-way SMS between properties and their customers and prospects.
* **`POST /messages`** — Send an SMS to a customer or prospect. Requires an `idempotencyId`; retries with the same key return the original message rather than sending a duplicate.
* **`GET /messages/{messageId}`** — Read a single message, including delivery status and attachments.
* **`GET /messages`** — List messages by `conversationId`, `idempotencyId`, or `messageSid`.
* **`GET /conversations`** — List conversations by recipient, by household, or by property.

**Notes for Integrators**

* Delivery is asynchronous. A `201` from `POST /messages` means the message was accepted, not delivered — poll until `messageStatus` reaches `DELIVERED`, `UNDELIVERED`, or `FAILED`.
* `messageStatus` values reported by the carrier are passed through verbatim, so treat the field as an open set of strings.
* `content` is limited to 1600 characters, and attachment download URLs expire 15 minutes after they are issued.
* `messages` and `conversations` are separate permission scopes on an API key and are granted independently.

> **Note**
>
> These endpoints are available by request. To explore availability for your integration, please contact our team.

---

## June 2026

### Schema & Documentation Updates - June 22, 2026

#### Certifications Export

**New `exported_certifications` Export Topic Documentation**

* Added dedicated [Certifications Export](/exports-certifications) documentation page covering all fields, certification type mappings, program normalization rules, and soft-delete behavior.
* Added the topic to the Data Exports Integration navigation.

**New Columns on `exported_certifications`**

* **`unitId`** (UUID) — ID of the unit from the certification's tenant event unit record.
* **`unitNumber`** (varchar) — Human-readable unit number resolved from the tenant event; falls back to the Unit table when absent.
* **`tenantName`** (varchar) — Full name of the head of household derived from the latest non-deleted `TenantEventPerson` record.
* **`certificationType`** (varchar) — Normalized short code: `MOVE_IN` → `MI`, annual → `AR`, interim → `IR`.
* **`dueDate`** (date) — Recertification due date (`recertificationDate`).
* **`program`** (varchar) — Normalized program name: `HUD`, `Tax Credit` (matches `LIHTC`/`LITHC`), `Rural Development`, or source value.
* **`currentStatus`** (varchar) — Compliance approval status normalized to upper snake case.

**Soft-Delete Hardening**

* Added `deletedAt IS NULL` guards to all joins: `TenantEvent`, `TenantEventUnit`, `Unit`, `TenantEventPerson`, `SetAsideProgram`, `ComplianceApprovalStatus`, `RentalAssistanceSource`, and secondary subsidy lookups.
* Household and Property joins are inner joins requiring non-deleted records.

### Database View Updates - June 16, 2026

#### ExportedCertifications View

**New Columns for Compliance Reporting:**

* **`householdIncomeAmount`** (number) - Total household income as recorded on the certification, in USD
* **`marketRentAmount`** (number) - Market rent amount used for calculations

**Impact on API:** The `/certifications` endpoint now includes these fields in API responses and data exports.

---

## May 2026

### Documentation & API Updates - May 21, 2026

#### Screenings API

**New `previousScreening` Field on Applicants**

* Added `previousScreening` object to each applicant in the `GET /screening-requests/{id}` response.
* When an applicant has a prior screening on the same property and provider, `previousScreening` is populated; otherwise it is `null`.
* **Object fields:** `id`, `requestId`, `resultId`, `status`, `screeningCreatedAt`, `resultCreatedAt`, `cancellationReasons`.
* `cancellationReasons` is always `null` at this time and reserved for future use.
* **Impact on API:** Additive only — no existing fields changed. Consumers that do not read `previousScreening` are unaffected.
* **Use Case:** Enables screening providers to detect prior screenings per applicant and make their own deduplication or re-screening decisions (e.g., mirror prior results if screened within threshold, re-screen fresh if outside threshold, or screen as new if `previousScreening` is `null`).

---

## April 2026

### Documentation & API Updates - April 2, 2026

#### ILS Prospect API

**New ILS Prospect Creation Endpoint**

* Added OpenAPI documentation for ILS prospect creation endpoint: `/external-api/ils-prospects`.
* Added a dedicated API reference section for ILS Prospects, including operation details, request body schema, and response models.
* Added ILS Prospects overview documentation and linked it in navigation.
* Updated the ILS Integration menu to include Prospect Creation API reference content.
* **Impact on API:** ILS partners now have clear, production URL-based documentation for prospect ingestion.
* **Use Case:** Enables ILS vendors to submit prospect leads directly into Fortress using a dedicated API key.

---

## March 2026

### Schema Updates - March 19, 2026

#### Work Orders API

**New Priority Value: `WARRANTY`**

* Added `WARRANTY` to the `priority` enum for work orders.
* Applies to both read models and write payloads (create/update).
* **Impact on API:** Work order endpoints now accept and return warranty-prioritized work orders.
* **Use Case:** Enables explicit tracking of warranty-related maintenance requests.

---

## February 2026

### Documentation Updates - February 20, 2026

#### New ILS Integration Section

**Unit Availability Response Model Documentation**

* Added a comprehensive new section covering ILS integrations, following the MITS 4.1 standard.
* Includes:
  * Overview of ILS integration and XML response structure
  * Annotated XML skeleton for unit availability responses
  * Field reference table with schema details and enumerations (adapted from MITS Core Data 4.0)
  * Example XML response for integrators
  * Key element descriptions and integration notes
* Navigation updated to include the new ILS section.
* Documentation designed to help integrators parse, validate, and utilize unit availability data from Fortress API.

**Schema Reference:**

* Field-level details and enumerations are based on the MITS Core Data 4.0 schema ([rettc.org](https://rettc.org)).
* Links to the schema and example files provided for further reference.

---

## December 2025

### Documentation Updates - December 5, 2025

#### API Documentation Enhancements

**Availability Indicators**

* Added "Available by Request" badges and notes to restricted endpoints:
  * Move-ins (POST, PUT, DELETE)
  * Move-outs (POST, PUT, DELETE)
  * Document uploads (POST)
  * Property groups (POST, PUT, DELETE)
  * Users (POST, PUT, DELETE)
  * Leases (DELETE - cancellation)
  * Transactions (POST)
  * Scheduled transactions (POST)

**Schema Accuracy Updates**

* **Prospects:** Updated schema to match implementation
  * Made most fields optional (only `propertyId` required)
  * Added `isCorporation` field
  * Removed internal `bypassDuplicateCheck` field
  * Improved validation patterns for phone and email
* **Transactions:** Updated schema to reflect optional fields
  * All fields now optional with proper validation
  * Added `transactionId` to response
  * Clarified `householdId`/`applicationId` requirement
* **Scheduled Transactions:** Corrected schema definitions
  * Updated type enum to `[CHARGE, CREDIT]` only
  * Added `frequency` and `documentNumber` fields
  * Added pattern validation for date fields
  * Documented SECURITY ledger limitation

**Deprecation Notices**

* Marked all report endpoints as deprecated (GET, POST, DELETE)
* Marked all scheduled report endpoints as deprecated
* Marked all turn endpoints as deprecated (GET, POST, PUT, DELETE)
* Added deprecation badges and notes to affected endpoints

**Documentation Organization**

* Alphabetically sorted all API paths in OpenAPI specification
* Alphabetically sorted API reference navigation
* Created new overview pages:
  * Reversals overview
  * Scheduled Reports overview
  * Turns overview
* Updated all existing overview pages with complete endpoint listings

**Screening API Documentation**

* Added "Onboarding Required" badges to all screening endpoints
* Created comprehensive integration flow guide with:
  * Step-by-step integration process
  * Code examples for each step
  * Visual Mermaid sequence diagram showing Provider-API-UI interaction
  * Important notes about screening method matching and status handling

**Applications API Documentation**

* Created comprehensive integration flow guide for external application providers
* Added visual Mermaid sequence diagram showing complete application lifecycle
* Documented 7-step integration process:
  * Create Prospect
  * Qualify & Validate Prospect
  * Create Application From Prospect
  * Assign Unit
  * Application Management (status, applicants, pets, fees)
  * Lease Sync & Document Upload
  * Household Completion Rules
* Added code examples with curl commands for key operations
* Included best practices and expectations section covering:
  * ID management and storage requirements
  * Status synchronization guidelines
  * Unit availability validation
  * Data quality standards
  * Reliability and performance optimization
  * Financial transaction handling
  * Document management requirements
* Updated endpoint reference table with all 14 application-related endpoints

---

## November 2025

### Database View Updates - November 26, 2025

#### ExportedCertifications View

**New Columns:**

* **`contractNumber`** (string) - Contract number from floor plan affordable program
* **`secondarySubsidyType`** (string) - Name of the secondary subsidy type for the property
* **`ownerSignatureDate`** (timestamp) - Date when the owner signed the certification
* **`customerSignatureDate`** (timestamp) - Date when the head of household signed the certification
* **`customerSignatureSuccess`** (boolean) - Indicates if all lease signers were able to sign

**Impact on API:** The `/certifications` endpoint now includes these fields for enhanced affordable housing compliance tracking
**Use Case:** Enables comprehensive HUD certification reporting and signature tracking workflows

#### ExportedIncomeLimits View

**New Column: `effectiveDate`**

* **Type:** `date`
* **Description:** Effective date of the income limit for the set-aside program
* **Impact on API:** The `/incomeLimits` endpoint now includes the effective date for each income limit record
* **Use Case:** Enables tracking of when income limits became active and historical income limit analysis

### Database View Updates - November 25, 2025

#### ExportedActivities View

**Enhanced Activity Types and Status Tracking**

**Modified Column: `type`**

* **Description:** Improved activity type classification with more granular status differentiation
* **New Values:**
  * `VISITED` - Corrected spelling from "VISTED"
  * `VISIT_CANCELLED` - Specific to visit activities
  * `VISIT_NO_SHOW` - Specific to visit activities
  * `TOUR_CANCELLED` - Specific to tour activities
  * `TOUR_NO_SHOW` - Specific to tour activities
* **Change:** Activity type now differentiates between tour and visit cancellations/no-shows

**New Column: `assignedUserId`**

* **Type:** `uuid`
* **Description:** References the user assigned to the activity
* **Impact on API:** The `/activities` endpoint now includes assignment information
* **Use Case:** Enables activity tracking by assigned user and workload reporting

#### ExportedApplications View

**New Columns for Application Form Completion Tracking**

**New Column: `primaryFormCompletedAt`**

* **Type:** `timestamp`
* **Description:** Most recent completion timestamp of the primary form for any applicant on the application
* **Impact on API:** The `/applications` endpoint now tracks when primary application forms were completed
* **Use Case:** Enables monitoring of application completion progress and processing timelines

**New Column: `secondaryFormCompletedAt`**

* **Type:** `timestamp`
* **Description:** Most recent completion timestamp of the secondary form for any applicant on the application
* **Impact on API:** The `/applications` endpoint now tracks when secondary application forms were completed
* **Use Case:** Supports detailed application workflow tracking and compliance reporting

### Database View Updates - November 17, 2025

#### ExportedCustomers View

**New Column: `relationship`**

* **Type:** `string` (enum)
* **Description:** Indicates the relationship of the customer to the head of household
* **Possible Values:**
  * `PARTNER` - Partner/spouse relationship
  * `PARENT` - Parent of the head of household
  * `ADULT_CHILD` - Adult child of the head of household
  * `ROOMMATE` - Roommate relationship
  * `CHILD` - Minor child
  * `GUARANTOR` - Lease guarantor
  * `OTHER` - Other relationships
  * `null` - For head of household (no relationship to self)
* **Impact on API:** The `/customers` endpoint now includes this field in API responses and data exports
* **Use Case:** Enables filtering and reporting based on household member relationships

### Schema Updates - November 14, 2025

#### ScreeningRequest Schema

**New Field: `socialSecurityNumber`**

* **Type:** `string`
* **Description:** Social Security Number for applicant employment information (secured and encrypted)
* **Location:** Within the `employmentInformation` object in screening requests
* **Impact on API:** The screening request payload now accepts SSN as part of employment verification data
* **Security:** All SSN data is encrypted in transit and at rest
* **Use Case:** Enables more comprehensive background screening and employment verification

### Database View Updates - November 13, 2025

#### ExportedUserProperties View

**New View Created**

* **Description:** Exposes user-to-property associations for access control and permission management
* **Columns:**
  * `id` (uuid) - Unique identifier for the user property association
  * `userId` (uuid) - Reference to the user
  * `propertyId` (uuid) - Reference to the property
  * `organizationId` (uuid) - Reference to the organization (joined from Property)
  * `createdAt` (timestamp) - When the association was created
  * `updatedAt` (timestamp) - When the association was last updated
* **Impact on API:** New `/userProperties` endpoint available for querying user property access
* **Use Case:** Enables tracking which users have access to which properties and supports multi-property user management

### Database View Updates - November 12, 2025

#### ExportedLeases View

**New Column: `moveOutId`**

* **Type:** `uuid`
* **Description:** References the most recent Notice to Vacate (move out) ID associated with the lease
* **Impact on API:** The `/leases` endpoint now includes this field in API responses and data exports
* **Use Case:** Enables direct tracking of move out notices and lease termination workflows

#### ExportedScheduledTransactions View

**New Column: `description`**

* **Type:** `string`
* **Description:** Human-readable description of the transaction code from the master transaction code table
* **Impact on API:** The `/scheduledTransactions` endpoint now includes this field in API responses and data exports
* **Use Case:** Provides clearer context for scheduled charges without requiring separate lookups

**Enhanced Lease Renewal Logic**

* **Description:** Updated view logic to properly track scheduled transactions through lease renewals
* **Impact on API:** Scheduled transactions now correctly reference the most recent active lease when renewals exist, and only include transactions for moved-in leases
* **Use Case:** Ensures scheduled transactions remain accurate and accessible through the lease lifecycle

### Database View Updates - November 7, 2025

#### ExportedCustomers View

**New Column: `relationship`**

* **Type:** `string` (enum)
* **Description:** Indicates the relationship of the customer to the head of household
* **Possible Values:**
  * `PARTNER` - Partner/spouse relationship
  * `PARENT` - Parent of the head of household
  * `ADULT_CHILD` - Adult child of the head of household
  * `ROOMMATE` - Roommate relationship
  * `CHILD` - Minor child
  * `GUARANTOR` - Lease guarantor
  * `OTHER` - Other relationships
  * `null` - For head of household (no relationship to self)
* **Impact on API:** The `/customers` endpoint now includes this field in API responses and data exports
* **Use Case:** Enables filtering and reporting based on household member relationships

#### ExportedUnits View

**New Column: `reportConfigurationValue`**

* **Type:** `string`
* **Description:** Contains the report configuration value for each unit based on property settings
* **Possible Values:**
  * `Quoting Rent`
  * `Financial Market Rent`
  * `Market Rent`
* **Impact on API:** The `/units` endpoint now includes this field in API responses and data exports
* **Use Case:** Enables property-specific rent reporting configurations and filtering by rent type

---

## October 2025

### Database View Updates - October 31, 2025

#### ExportedLeases View

**New Column: `documentId`**

* **Type:** `uuid`
* **Description:** References the document ID associated with each lease
* **Impact on API:** The `/leases` endpoint now includes this field in API responses and data exports
* **Use Case:** Enables direct document tracking and retrieval for lease records

### Database View Updates - October 24, 2025

#### ExportedCustomers View

**Enhanced Household Relationships**

**New Column: `headOfHousehold`**

* **Type:** `boolean`
* **Description:** Indicates whether the customer is the head of household
* **Impact on API:** The `/customers` endpoint now includes this field in API responses and data exports
* **Use Case:** Simplifies identification of primary household members

**Modified Column: `householdId`**

* **Description:** Now properly associates customers with households through the applicant relationship
* **Impact on API:** The `/customers` endpoint now returns household associations for all household members in API responses and data exports (previously only available for primary household members)
* **Change:** This field now populates for all household members, not just the primary customer

---

## Breaking Changes

### ExportedCustomers View - October 24, 2025

⚠️ **Potential Breaking Change**

The `householdId` field has been updated to use the applicant relationship instead of direct household association. This may affect:

* Queries filtering by `householdId` for non-primary household members
* Data exports relying on the previous household association logic
* Reports that aggregate customer data by household

**Migration:** Review any queries or integrations using the `householdId` field from the customers endpoint to ensure they still return expected results.

---

## Schema Reference

All database views are located in `/db/views/` and are synchronized to the API through the data export layer. Changes to these views are automatically reflected in the corresponding API endpoints:

* `ExportedUnits.sql` → `/units` endpoint
* `ExportedLeases.sql` → `/leases` endpoint
* `ExportedCustomers.sql` → `/customers` endpoint
* `ExportedScheduledTransactions.sql` → `/scheduledTransactions` endpoint
* `ExportedUserProperties.sql` → `/userProperties` endpoint

---

## Support

For questions about these changes or to report issues:

* Email: [integrations@fortresstech.io](mailto:integrations@fortresstech.io)
* Check our [API Reference](/api-reference) for detailed endpoint documentation