> For the complete documentation index, see [llms.txt](https://api-docs.realfinity.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://api-docs.realfinity.io/mcp/tool-reference/tasks-and-conditions.md).

# Tasks & Conditions

Tools for the work items attached to a loan: what the borrower still owes, what the internal team still owes, which tasks across the whole pipeline need attention, and which underwriting condition categories show up on loans that never close.

{% hint style="info" %}
**Results are scoped to the calling user.** Every tool on this page returns only the loans the authenticated user may see. Scope is resolved from SQL on every request, from the caller's roles — never from anything in the request itself.
{% endhint %}

**Scope by role**

| Role                      | Scope                                                                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| Admin                     | All loans                                                                                                                                        |
| Processor                 | All loans, org-wide — **not** limited to their own company. Two exceptions on this page: `search_loan_tasks` and `conditions_by_outcome`, below. |
| ConciergeKey              | Loans of managed agents                                                                                                                          |
| Agent / DualLicensedAgent | Loans they originate (self, assisted, ConciergeKey) or referred                                                                                  |
| Pre-licensed              | Referred loans only                                                                                                                              |

{% hint style="warning" %}
**A Processor is not company-bounded for per-loan access.** `get_loan_application_user_tasks` and `get_loan_application_borrower_tasks` give a Processor the same reach as an Admin: the tasks on every loan in the organization, whichever company the loan belongs to. The company-bounded branch in the loan authorization check belongs to the separate **CompanyAdmin** role, not to Processor.

The aggregate path narrows a Processor differently. `search_loan_tasks` and `conditions_by_outcome` on this page — and the caller-scoped reporting tools on [Reporting](/mcp/tool-reference/reporting.md) — resolve scope through the accessible-users set, which for a Processor is the users of their own company. So a Processor can open the task list of any loan by id, but a `search_loan_tasks` result set covers their company only.
{% endhint %}

Officer, processor, and referrer **name** fields require Admin, Processor, or ConciergeKey. An empty result is normally scoping, not an error.

**User tasks vs. borrower tasks — the distinction that matters most**

Every task on a Realfinity loan is assigned to exactly one side. The assignment lives on the task's `TaskAssignmentType`, and the two tools split cleanly on it. They never overlap.

|                    | `get_loan_application_user_tasks`                                                | `get_loan_application_borrower_tasks`                                                    |
| ------------------ | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Whose work         | Internal staff — loan officer, processor, underwriting-condition owners          | The borrower                                                                             |
| Assignment type    | `User`                                                                           | `Borrower`                                                                               |
| Typical items      | Condition follow-ups, internal review steps, work items assigned to a named user | Document requests, e-sign requests, questions, conditions passed through to the borrower |
| Answers            | "What does my team still owe on this loan?"                                      | "What is the borrower still waiting to give us?"                                         |
| Whose view you get | Depends on your role (see that tool's notes)                                     | The whole borrower to-do list                                                            |

**Which tool to reach for**

| You have                 | You want                                                                                | Tool                                  |
| ------------------------ | --------------------------------------------------------------------------------------- | ------------------------------------- |
| One loan id              | The internal team's work items on it                                                    | `get_loan_application_user_tasks`     |
| One loan id              | The borrower's outstanding to-dos                                                       | `get_loan_application_borrower_tasks` |
| No loan in mind          | Internal tasks/conditions across every loan you can see, filtered by status or category | `search_loan_tasks`                   |
| A book of finished loans | Which condition categories cluster on loans that failed rather than closed              | `conditions_by_outcome`               |

Both per-loan tools take a loan identifier, not a borrower or a name. If you only have a borrower name, an email, or an address, find the loan first with the tools on [Loan Lookup](/mcp/tool-reference/loan-lookup.md).

***

### get\_loan\_application\_user\_tasks

Retrieve the internal user-assigned tasks (loan officer and processor work items) for a Realfinity loan application, identified by either its MLM ExternalId (preferred when the loan is in MLM) or its internal Realfinity GUID.

* **Backing store:** SQL
* **Roles:** Admin, Agent, Processor, ConciergeKey, DualLicensedAgent

**Parameters**

| Name                | Type   | Required                                      | Description                                                                                                                                                                                                                                                                                            |
| ------------------- | ------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `externalId`        | string | Optional — but one of the two ids is required | PREFERRED when available. The MLM ExternalId of the loan (string, up to 108 chars). Use this whenever the loan already exists in MLM — it is the stable identifier humans see in MLM UIs and is what webhooks surface. If both ids are supplied, `externalId` wins and `loanApplicationId` is ignored. |
| `loanApplicationId` | GUID   | Optional — but one of the two ids is required | Fallback identifier: the Realfinity internal loan application ID (GUID). Use this only when no `externalId` is known — typically because the loan has not yet been pushed to MLM.                                                                                                                      |

The loan is resolved by MLM external id or internal id and access-checked before any task data is returned: `Bad request:` when neither id is supplied, `Not found:` when the id matches no non-deleted loan, `Unauthorized:` when the loan is outside your scope.

**What your role changes about the result**

This tool mirrors the Private API's `TasksController.GetUserTasksAsync`, and it replicates that endpoint's two role-driven flags rather than inventing its own rules. Neither flag is a parameter — both are computed server-side from your token's roles, so two callers asking about the same loan can legitimately get different lists.

| Flag                    | Set when you are                  | Effect on what comes back                                                                                                                                                                                                 |
| ----------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CanAccessAllUserTasks` | Admin, Processor, or ConciergeKey | **On:** every user task on the loan, whoever it is assigned to. **Off:** only tasks whose `assignedToUser` is you — an Agent sees their own work items and nothing else, so a short list does not mean the loan is clean. |
| `IncludeUserConditions` | DualLicensedAgent                 | **On:** underwriting condition tasks that are *not* applied to the borrower (`conditionAppliesToBorrower != true`) are unioned in, even when they are not assigned to you. **Off:** those conditions are absent.          |

The two combine. A DualLicensedAgent who is not also Admin/Processor/ConciergeKey gets *their own* user tasks plus *all* non-borrower-facing conditions.

**Returns**

`GetLoanApplicationTasksResponse` — `loanApplicationId` plus `tasks[]`, a list of `LoanApplicationTaskItem`. Each task carries:

* Identity and timing — `id`, `name`, `description`, `code`, `displayOrder`, `createdTimestamp`, `updatedTimestamp`, `taskStatusTimestamp`
* Classification — `type` (`TaskType`), `item` (`Item`), `taskAssignmentType`, `status` (`TaskStatus`, with `id`/`code`/`name`)
* Condition detail — `conditionNo`, `conditionCategory`, `conditionDueDate`, `conditionAssignedUserFullName`, `conditionBorrowerFullName`, `conditionAppliesToBorrower`
* Assignment — `assignedToUser` (`LoanAppUser`), `assignedToBorrowerId`
* Response/outcome — `document`, `rejectionReason`, `rejectionReasonNote`, `creditReportType`, `questionType`, `yesNoResponse`, `shortAnswer`, `notes`
* Flags — `isRequired`, `isForPrimaryBorrower`, `isAccountMainTask`, `isSubscribed`

<details>

<summary>Example response</summary>

Synthetic and redacted — every identifier below is a placeholder, not data from a real loan.

```json
{
  "loanApplicationId": "00000000-0000-0000-0000-000000000000",
  "tasks": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "name": "Verify employment gap explanation",
      "description": "Underwriting requires a written explanation for the 2024 employment gap.",
      "code": "CONDITION",
      "status": { "id": 3, "code": "Submitted", "name": "Submitted" },
      "type": { "code": "Condition", "name": "Condition" },
      "taskAssignmentType": { "code": "User", "name": "User" },
      "conditionNo": 14,
      "conditionCategory": "Income",
      "conditionDueDate": "2026-03-02",
      "conditionAssignedUserFullName": "Casey Processor",
      "conditionBorrowerFullName": "Jane Borrower",
      "conditionAppliesToBorrower": false,
      "assignedToUser": {
        "id": "00000000-0000-0000-0000-000000000000",
        "firstName": "Casey",
        "lastName": "Processor",
        "email": "casey@example.com"
      },
      "isRequired": true,
      "isSubscribed": false,
      "createdTimestamp": "2026-02-18T15:04:11Z",
      "updatedTimestamp": "2026-02-24T18:22:39Z"
    }
  ]
}
```

</details>

**Notes**

* Deleted tasks and asset-account verification tasks (`assetAccountId` set) are always excluded.
* An Agent-only caller getting an empty `tasks[]` most likely has no user tasks *assigned to them* on that loan — it is not evidence that the loan has no outstanding internal work. Ask an Admin, Processor, or ConciergeKey caller for the full picture, or use `search_loan_tasks`.
* `isSubscribed` reflects the calling user's status-change notification subscriptions and is `null` when not computed.
* For the borrower's side of the same loan, call `get_loan_application_borrower_tasks`. For wizard section progress, use `get_loan_application`'s `sectionCompletion` instead — verification tasks are deliberately not returned here.

***

### get\_loan\_application\_borrower\_tasks

Retrieve the borrower-assigned tasks (document requests, e-sign requests, questions, conditions) for a Realfinity loan application, identified by either its MLM ExternalId (preferred when the loan is in MLM) or its internal Realfinity GUID.

* **Backing store:** SQL
* **Roles:** Admin, Agent, Processor, ConciergeKey

**Parameters**

| Name                | Type   | Required                                      | Description                                                                                                                                                                                                                                                                                            |
| ------------------- | ------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `externalId`        | string | Optional — but one of the two ids is required | PREFERRED when available. The MLM ExternalId of the loan (string, up to 108 chars). Use this whenever the loan already exists in MLM — it is the stable identifier humans see in MLM UIs and is what webhooks surface. If both ids are supplied, `externalId` wins and `loanApplicationId` is ignored. |
| `loanApplicationId` | GUID   | Optional — but one of the two ids is required | Fallback identifier: the Realfinity internal loan application ID (GUID). Use this only when no `externalId` is known — typically because the loan has not yet been pushed to MLM.                                                                                                                      |

The loan is resolved and access-checked exactly as in `get_loan_application_user_tasks`, with the same errors.

**Returns**

`GetLoanApplicationTasksResponse` — the same shape as `get_loan_application_user_tasks`, filtered to tasks whose assignment type is `Borrower`. On these tasks the borrower-facing fields matter most: `assignedToBorrowerId`, `isForPrimaryBorrower`, `document`, `questionType`, `yesNoResponse`, `shortAnswer`, `rejectionReason`, and `rejectionReasonNote`.

<details>

<summary>Example response</summary>

Synthetic and redacted — every identifier below is a placeholder, not data from a real loan.

```json
{
  "loanApplicationId": "00000000-0000-0000-0000-000000000000",
  "tasks": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "name": "Most recent two pay stubs",
      "description": "Upload your two most recent pay stubs.",
      "code": "DOCUMENT",
      "status": { "id": 1, "code": "Requested", "name": "Requested" },
      "type": { "code": "Document", "name": "Document" },
      "taskAssignmentType": { "code": "Borrower", "name": "Borrower" },
      "assignedToBorrowerId": "00000000-0000-0000-0000-000000000000",
      "isForPrimaryBorrower": true,
      "isRequired": true,
      "document": null,
      "rejectionReason": null,
      "rejectionReasonNote": null,
      "createdTimestamp": "2026-02-18T15:04:11Z",
      "updatedTimestamp": "2026-02-18T15:04:11Z"
    },
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "name": "Have you had a bankruptcy in the last 7 years?",
      "code": "QUESTION",
      "status": { "id": 4, "code": "Accepted", "name": "Accepted" },
      "type": { "code": "Question", "name": "Question" },
      "taskAssignmentType": { "code": "Borrower", "name": "Borrower" },
      "assignedToBorrowerId": "00000000-0000-0000-0000-000000000000",
      "questionType": { "code": "YesNo", "name": "Yes / No" },
      "yesNoResponse": false,
      "shortAnswer": null,
      "isRequired": true,
      "createdTimestamp": "2026-02-18T15:04:11Z",
      "updatedTimestamp": "2026-02-19T11:47:02Z"
    }
  ]
}
```

</details>

**Notes**

* Role list differs from the user-tasks tool: **DualLicensedAgent is not authorized here.** That role reaches condition work through `get_loan_application_user_tasks` and its `IncludeUserConditions` flag instead.
* No role-driven filtering applies. Any authorized caller who can read the loan gets the borrower's whole to-do list.
* Three sets of tasks are excluded: deleted tasks, asset-account verification tasks, and the borrower wizard's own **Loan Application** tasks (section verification, Loan App Questions). For wizard progress read `get_loan_application`'s `sectionCompletion`.
* An empty `tasks[]` means the borrower has nothing outstanding, or that everything on the loan is assigned to internal users instead.

***

### search\_loan\_tasks

Search loan tasks and underwriting conditions across loans (filter by task status such as "Submitted"/"Requested", condition category, assignee, and free text). Use this for processing questions like "which loans have tasks that need review".

* **Backing store:** SQL
* **Roles:** Admin, Processor, DualLicensedAgent, ConciergeKey, Agent

**Parameters**

| Name                 | Type    | Required | Description                                                                                                                                                                                                                                                                                                            |
| -------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter`             | object  | Optional | Loan filter. Date ranges, statuses, and officer/agent scoping apply; loan-amount/commission filters are ignored for tasks. Only `filter.searchText` is passed through to the task query (see notes).                                                                                                                   |
| `taskStatus`         | string  | Optional | Task status to filter by, e.g. `"Requested"`, `"Submitted"`, `"Accepted"`, `"Rejected"`. Useful for finding tasks/conditions that need review. Matched against the status **code**, exactly.                                                                                                                           |
| `conditionCategory`  | string  | Optional | Underwriting condition category to filter by. Exact match.                                                                                                                                                                                                                                                             |
| `assignedToUserName` | string  | Optional | Filter to tasks assigned to a user whose name matches this text. Case-insensitive substring, matched against either the condition's assigned-user full name or the assigned user's first + last name.                                                                                                                  |
| `top`                | integer | Optional | Max tasks to return (default 25). A value of 0 or less falls back to 25.                                                                                                                                                                                                                                               |
| `skip`               | integer | Optional | Number of tasks to skip for paging (default 0). Negative values are clamped to 0.                                                                                                                                                                                                                                      |
| `orderBy`            | string  | Optional | Sort order passed through to the tasks pipeline. Accepted values, each as `<field> asc` or `<field> desc`: `updatedtimestamp`, `createdtimestamp`, `name`, `status`, `duedate`, `loannumber`, `borrower`, `loanofficer`, `category`, `assigned`. Anything unrecognized silently falls back to `updatedtimestamp desc`. |

`filter.searchText` is a free-text match, with whitespace-separated tokens that must **all** match, against the loan number, the task name, and borrower first/last names.

**Returns**

`GetTasksPipelineResponse` — `count` (total matches before paging) and `value[]`, a list of `TaskPipelineItem`. Each item is a `LoanApplicationTaskItem` plus loan context: `loanApplicationId`, `externalLoanNumber`, `borrowers[]` (names), `agent` (the loan officer), and `referredBy`. The populated fields are `id`, `loanApplicationId`, `externalLoanNumber`, `name`, `conditionCategory`, `conditionNo`, `conditionDueDate`, `conditionAssignedUserFullName`, `conditionAppliesToBorrower`, `conditionBorrowerFullName`, `status`, `borrowers`, `agent`, `referredBy`, `createdTimestamp`, and `updatedTimestamp`.

<details>

<summary>Example response</summary>

Synthetic and redacted — every identifier below is a placeholder, not data from a real loan.

```json
{
  "count": 37,
  "value": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "loanApplicationId": "00000000-0000-0000-0000-000000000000",
      "externalLoanNumber": "LN-000000",
      "name": "Verify employment gap explanation",
      "conditionCategory": "Income",
      "conditionNo": 14,
      "conditionDueDate": "2026-03-02",
      "conditionAssignedUserFullName": "Casey Processor",
      "conditionAppliesToBorrower": false,
      "conditionBorrowerFullName": "Jane Borrower",
      "status": { "id": 3, "code": "Submitted", "name": "Submitted" },
      "borrowers": ["Jane Borrower", "John Borrower"],
      "agent": {
        "id": "00000000-0000-0000-0000-000000000000",
        "firstName": "Robin",
        "lastName": "Officer",
        "email": "robin@example.com"
      },
      "referredBy": null,
      "createdTimestamp": "2026-02-18T15:04:11Z",
      "updatedTimestamp": "2026-02-24T18:22:39Z"
    }
  ]
}
```

</details>

**Notes**

* This searches **user-assigned tasks only** (assignment type `User`). Borrower to-dos never appear here — for those, call `get_loan_application_borrower_tasks` on a specific loan.
* `count` is the total number of matching tasks, not the number returned in `value[]`. Page with `skip`/`top`.
* Scope is applied by loan, exactly as in the scoping callout: your accessible loan-officer set **or** loans you referred. Processor scope is encoded through that accessible set — which for a Processor includes their own company's users — rather than through the org-wide per-loan branch.
* Test-officer loans are excluded unless the calling user is itself flagged as test data.
* `taskStatus` and `conditionCategory` are exact matches, so a near-miss returns zero rows rather than a fuzzy result. `assignedToUserName` is the only substring filter.
* The `filter` object is shared with the reporting tools, so it accepts more fields than this tool uses. Loan status, date-range, and amount fields on it do not narrow the task query.

***

### conditions\_by\_outcome

Correlate underwriting condition **categories** with terminal loan **outcomes**. For loans in terminal statuses (closed/funded vs. denied/withdrawn/cancelled/suspended/archived), returns per-category counts and the share associated with non-closing loans (`failedShare`). Answers "which underwriting conditions are most indicative of a loan that won't close".

{% hint style="warning" %}
**This is not a pricing tool.** "Conditions" here means *underwriting conditions* — the stipulations a file must clear — not loan terms, rate conditions, or product eligibility. Nothing about pricing, rates, or products is involved. For pricing, use the tools on [Pricing](/mcp/tool-reference/pricing.md).
{% endhint %}

* **Backing store:** SQL
* **Roles:** Admin, Processor, DualLicensedAgent, ConciergeKey, Agent

This is the exception on the reporting side: `report_loans`, `loan_pipeline_report`, and `query_loans` all run over the denormalized Cosmos loan snapshot, but `conditions_by_outcome` joins `LoanApplicationTasks` to `LoanApplications` directly in SQL, because condition-level task rows are not part of the Cosmos snapshot.

**Parameters**

| Name     | Type   | Required | Description                                                                                                                            |
| -------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | object | Optional | Loan filter applied before correlating conditions to outcomes. All fields optional; results are always scoped to loans you can access. |

Only the filter fields that map cleanly to SQL are honoured here: `loanOfficerIds`, `referringAgentId`, `statusDateFrom`, and `statusDateTo`. Other `filter` fields (loan types, purposes, amount and commission ranges, closing dates, property state, free text) are ignored by this tool.

**Returns**

`ConditionOutcomeResponse`:

| Field             | Contents                                                                                                  |
| ----------------- | --------------------------------------------------------------------------------------------------------- |
| `closedLoanTotal` | Distinct closed/funded loans **that carry at least one underwriting condition** in scope                  |
| `failedLoanTotal` | Distinct failed loans that carry at least one condition in scope                                          |
| `categories[]`    | One `ConditionOutcomeRow` per condition category                                                          |
| `note`            | Server-supplied caveat restating what "failed" means and that `failedShare` is association, not causation |

Each `ConditionOutcomeRow` carries `conditionCategory`, `closedLoanCount`, `failedLoanCount`, `closedConditionCount`, `failedConditionCount`, and `failedShare` (`failedLoanCount / (failedLoanCount + closedLoanCount)`, or `null` when the denominator is 0). Rows are sorted by `failedShare` descending, then `failedLoanCount` descending — so the categories most associated with loans that did not close come first. Conditions with no category are grouped under `(uncategorized)`.

Which statuses count as which outcome comes from `LoanPipelineStages`:

| Outcome | Statuses                                                                                              |
| ------- | ----------------------------------------------------------------------------------------------------- |
| Closed  | Loan Funded, Loan Closed, Loan Recorded, Loan Purchased, Loan Sold                                    |
| Failed  | Loan Denied, Loan Withdrawn, Loan Canceled, Loan Suspended, Loan Archived, Lead Cancel, Lead Declined |

<details>

<summary>Example response</summary>

Synthetic — this response carries no borrower or user data at all, so the figures below are illustrative only.

```json
{
  "closedLoanTotal": 412,
  "failedLoanTotal": 96,
  "categories": [
    {
      "conditionCategory": "Credit",
      "closedLoanCount": 31,
      "failedLoanCount": 44,
      "closedConditionCount": 48,
      "failedConditionCount": 71,
      "failedShare": 0.5867
    },
    {
      "conditionCategory": "Income",
      "closedLoanCount": 188,
      "failedLoanCount": 62,
      "closedConditionCount": 402,
      "failedConditionCount": 131,
      "failedShare": 0.248
    },
    {
      "conditionCategory": "(uncategorized)",
      "closedLoanCount": 97,
      "failedLoanCount": 11,
      "closedConditionCount": 140,
      "failedConditionCount": 14,
      "failedShare": 0.1019
    }
  ],
  "note": "Failed = denied / withdrawn / cancelled / suspended / archived. FailedShare is a descriptive association (not causation); only loans carrying underwriting conditions are counted."
}
```

</details>

**Notes**

* Only **terminal** loans are considered. Loans still in flight are excluded entirely, so the totals here are smaller than any pipeline count.
* Only loans **carrying underwriting conditions** are counted. A loan that closed without a single condition contributes nothing, which is why `closedLoanTotal` is not the closed-loan count you would get from `report_loans`.
* `failedShare` is descriptive association, never causation. A high share means the category co-occurs with non-closing loans in your book, not that the condition caused the failure. Small `closedLoanCount + failedLoanCount` denominators produce noisy shares — read the counts alongside the share.
* The condition-row scan is capped at 100,000 rows, so an extremely broad unfiltered call over a very large book can be truncated. Narrow with `statusDateFrom`/`statusDateTo` when the numbers look implausibly flat.
* Test-officer loans are excluded unless the calling user is itself flagged as test data.
* To go from a suspicious category to the individual conditions behind it, pass that category to `search_loan_tasks` as `conditionCategory`.
